#include <petsc/private/kspimpl.h> /*I "petscksp.h" I*/
#include <petscblaslapack.h>

typedef struct {
  PetscInt         method; /* 1, 2 or 3 */
  PetscInt         curl;   /* Current number of basis vectors */
  PetscInt         maxl;   /* Maximum number of basis vectors */
  PetscBool        monitor;
  PetscScalar     *alpha;  /* */
  Vec             *xtilde; /* Saved x vectors */
  Vec             *btilde; /* Saved b vectors, methods 1 and 3 */
  Vec              Ax;     /* method 2 */
  Vec              guess;
  PetscScalar     *corr;         /* correlation matrix in column-major format, method 3 */
  PetscReal        tol;          /* tolerance for determining rank, method 3 */
  Vec              last_b;       /* last b provided to FormGuess (not owned by this object), method 3 */
  PetscObjectState last_b_state; /* state of last_b as of the last call to FormGuess, method 3 */
  PetscScalar     *last_b_coefs; /* dot products of last_b and btilde, method 3 */
} KSPGuessFischer;

static PetscErrorCode KSPGuessReset_Fischer(KSPGuess guess)
{
  KSPGuessFischer *itg  = (KSPGuessFischer *)guess->data;
  PetscLayout      Alay = NULL, vlay = NULL;
  PetscBool        cong;

  PetscFunctionBegin;
  itg->curl = 0;
  /* destroy vectors if the size of the linear system has changed */
  if (guess->A) PetscCall(MatGetLayouts(guess->A, &Alay, NULL));
  if (itg->xtilde) PetscCall(VecGetLayout(itg->xtilde[0], &vlay));
  cong = PETSC_FALSE;
  if (vlay && Alay) PetscCall(PetscLayoutCompare(Alay, vlay, &cong));
  if (!cong) {
    PetscCall(VecDestroyVecs(itg->maxl, &itg->btilde));
    PetscCall(VecDestroyVecs(itg->maxl, &itg->xtilde));
    PetscCall(VecDestroy(&itg->guess));
    PetscCall(VecDestroy(&itg->Ax));
  }
  if (itg->corr) PetscCall(PetscMemzero(itg->corr, sizeof(*itg->corr) * itg->maxl * itg->maxl));
  itg->last_b       = NULL;
  itg->last_b_state = 0;
  if (itg->last_b_coefs) PetscCall(PetscMemzero(itg->last_b_coefs, sizeof(*itg->last_b_coefs) * itg->maxl));
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode KSPGuessSetUp_Fischer(KSPGuess guess)
{
  KSPGuessFischer *itg = (KSPGuessFischer *)guess->data;

  PetscFunctionBegin;
  if (!itg->alpha) PetscCall(PetscMalloc1(itg->maxl, &itg->alpha));
  if (!itg->xtilde) PetscCall(KSPCreateVecs(guess->ksp, itg->maxl, &itg->xtilde, 0, NULL));
  if (!itg->btilde && (itg->method == 1 || itg->method == 3)) PetscCall(KSPCreateVecs(guess->ksp, itg->maxl, &itg->btilde, 0, NULL));
  if (!itg->Ax && itg->method == 2) PetscCall(VecDuplicate(itg->xtilde[0], &itg->Ax));
  if (!itg->guess && (itg->method == 1 || itg->method == 2)) PetscCall(VecDuplicate(itg->xtilde[0], &itg->guess));
  if (!itg->corr && itg->method == 3) PetscCall(PetscCalloc1(itg->maxl * itg->maxl, &itg->corr));
  if (!itg->last_b_coefs && itg->method == 3) PetscCall(PetscCalloc1(itg->maxl, &itg->last_b_coefs));
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode KSPGuessDestroy_Fischer(KSPGuess guess)
{
  KSPGuessFischer *itg = (KSPGuessFischer *)guess->data;

  PetscFunctionBegin;
  PetscCall(PetscFree(itg->alpha));
  PetscCall(VecDestroyVecs(itg->maxl, &itg->btilde));
  PetscCall(VecDestroyVecs(itg->maxl, &itg->xtilde));
  PetscCall(VecDestroy(&itg->guess));
  PetscCall(VecDestroy(&itg->Ax));
  PetscCall(PetscFree(itg->corr));
  PetscCall(PetscFree(itg->last_b_coefs));
  PetscCall(PetscFree(itg));
  PetscCall(PetscObjectComposeFunction((PetscObject)guess, "KSPGuessFischerSetModel_C", NULL));
  PetscFunctionReturn(PETSC_SUCCESS);
}

/* Note: do not change the b right hand side as is done in the publication */
static PetscErrorCode KSPGuessFormGuess_Fischer_1(KSPGuess guess, Vec b, Vec x)
{
  KSPGuessFischer *itg = (KSPGuessFischer *)guess->data;
  PetscInt         i;

  PetscFunctionBegin;
  PetscCall(VecSet(x, 0.0));
  PetscCall(VecMDot(b, itg->curl, itg->btilde, itg->alpha));
  if (itg->monitor) {
    PetscCall(PetscPrintf(((PetscObject)guess)->comm, "KSPFischerGuess alphas ="));
    for (i = 0; i < itg->curl; i++) PetscCall(PetscPrintf(((PetscObject)guess)->comm, " %g", (double)PetscAbsScalar(itg->alpha[i])));
    PetscCall(PetscPrintf(((PetscObject)guess)->comm, "\n"));
  }
  PetscCall(VecMAXPY(x, itg->curl, itg->alpha, itg->xtilde));
  PetscCall(VecCopy(x, itg->guess));
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode KSPGuessUpdate_Fischer_1(KSPGuess guess, Vec b, Vec x)
{
  KSPGuessFischer *itg = (KSPGuessFischer *)guess->data;
  PetscReal        norm;
  int              curl = itg->curl, i;

  PetscFunctionBegin;
  if (curl == itg->maxl) {
    PetscCall(KSP_MatMult(guess->ksp, guess->A, x, itg->btilde[0]));
    /* PetscCall(VecCopy(b,itg->btilde[0])); */
    PetscCall(VecNormalize(itg->btilde[0], &norm));
    PetscCall(VecCopy(x, itg->xtilde[0]));
    PetscCall(VecScale(itg->xtilde[0], 1.0 / norm));
    itg->curl = 1;
  } else {
    if (!curl) {
      PetscCall(VecCopy(x, itg->xtilde[curl]));
    } else {
      PetscCall(VecWAXPY(itg->xtilde[curl], -1.0, itg->guess, x));
    }
    PetscCall(KSP_MatMult(guess->ksp, guess->A, itg->xtilde[curl], itg->btilde[curl]));
    PetscCall(VecMDot(itg->btilde[curl], curl, itg->btilde, itg->alpha));
    for (i = 0; i < curl; i++) itg->alpha[i] = -itg->alpha[i];
    PetscCall(VecMAXPY(itg->btilde[curl], curl, itg->alpha, itg->btilde));
    PetscCall(VecMAXPY(itg->xtilde[curl], curl, itg->alpha, itg->xtilde));
    PetscCall(VecNormalize(itg->btilde[curl], &norm));
    if (norm) {
      PetscCall(VecScale(itg->xtilde[curl], 1.0 / norm));
      itg->curl++;
    } else {
      PetscCall(PetscInfo(guess->ksp, "Not increasing dimension of Fischer space because new direction is identical to previous\n"));
    }
  }
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*
  Given a basis generated already this computes a new guess x from the new right hand side b
  Figures out the components of b in each btilde direction and adds them to x
  Note: do not change the b right hand side as is done in the publication
*/
static PetscErrorCode KSPGuessFormGuess_Fischer_2(KSPGuess guess, Vec b, Vec x)
{
  KSPGuessFischer *itg = (KSPGuessFischer *)guess->data;
  PetscInt         i;

  PetscFunctionBegin;
  PetscCall(VecSet(x, 0.0));
  PetscCall(VecMDot(b, itg->curl, itg->xtilde, itg->alpha));
  if (itg->monitor) {
    PetscCall(PetscPrintf(((PetscObject)guess)->comm, "KSPFischerGuess alphas ="));
    for (i = 0; i < itg->curl; i++) PetscCall(PetscPrintf(((PetscObject)guess)->comm, " %g", (double)PetscAbsScalar(itg->alpha[i])));
    PetscCall(PetscPrintf(((PetscObject)guess)->comm, "\n"));
  }
  PetscCall(VecMAXPY(x, itg->curl, itg->alpha, itg->xtilde));
  PetscCall(VecCopy(x, itg->guess));
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode KSPGuessUpdate_Fischer_2(KSPGuess guess, Vec b, Vec x)
{
  KSPGuessFischer *itg = (KSPGuessFischer *)guess->data;
  PetscScalar      norm;
  int              curl = itg->curl, i;

  PetscFunctionBegin;
  if (curl == itg->maxl) {
    PetscCall(KSP_MatMult(guess->ksp, guess->A, x, itg->Ax)); /* norm = sqrt(x'Ax) */
    PetscCall(VecDot(x, itg->Ax, &norm));
    PetscCall(VecCopy(x, itg->xtilde[0]));
    PetscCall(VecScale(itg->xtilde[0], 1.0 / PetscSqrtScalar(norm)));
    itg->curl = 1;
  } else {
    if (!curl) {
      PetscCall(VecCopy(x, itg->xtilde[curl]));
    } else {
      PetscCall(VecWAXPY(itg->xtilde[curl], -1.0, itg->guess, x));
    }
    PetscCall(KSP_MatMult(guess->ksp, guess->A, itg->xtilde[curl], itg->Ax));
    PetscCall(VecMDot(itg->Ax, curl, itg->xtilde, itg->alpha));
    for (i = 0; i < curl; i++) itg->alpha[i] = -itg->alpha[i];
    PetscCall(VecMAXPY(itg->xtilde[curl], curl, itg->alpha, itg->xtilde));

    PetscCall(KSP_MatMult(guess->ksp, guess->A, itg->xtilde[curl], itg->Ax)); /* norm = sqrt(xtilde[curl]'Axtilde[curl]) */
    PetscCall(VecDot(itg->xtilde[curl], itg->Ax, &norm));
    if (PetscAbsScalar(norm) != 0.0) {
      PetscCall(VecScale(itg->xtilde[curl], 1.0 / PetscSqrtScalar(norm)));
      itg->curl++;
    } else {
      PetscCall(PetscInfo(guess->ksp, "Not increasing dimension of Fischer space because new direction is identical to previous\n"));
    }
  }
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*
  Rather than the standard algorithm implemented in 2, we treat the provided x and b vectors to be spanning sets (not necessarily linearly independent) and use them to compute a windowed correlation matrix. Since the correlation matrix may be singular we solve it with the pseudoinverse, provided by SYEV/HEEV.
*/
static PetscErrorCode KSPGuessFormGuess_Fischer_3(KSPGuess guess, Vec b, Vec x)
{
  KSPGuessFischer *itg = (KSPGuessFischer *)guess->data;
  PetscInt         i, j, m;
  PetscReal       *s_values;
  PetscScalar     *corr, *work, *scratch_vec, zero = 0.0, one = 1.0;
  PetscBLASInt     blas_m, blas_info, blas_rank = 0, blas_lwork, blas_one = 1;
#if defined(PETSC_USE_COMPLEX)
  PetscReal *rwork;
#endif

  /* project provided b onto space of stored btildes */
  PetscFunctionBegin;
  PetscCall(VecSet(x, 0.0));
  m           = itg->curl;
  itg->last_b = b;
  PetscCall(PetscObjectStateGet((PetscObject)b, &itg->last_b_state));
  if (m > 0) {
    PetscCall(PetscBLASIntCast(m, &blas_m));
    blas_lwork = (/* assume a block size of m */ blas_m + 2) * blas_m;
#if defined(PETSC_USE_COMPLEX)
    PetscCall(PetscCalloc5(m * m, &corr, m, &s_values, blas_lwork, &work, 3 * m - 2, &rwork, m, &scratch_vec));
#else
    PetscCall(PetscCalloc4(m * m, &corr, m, &s_values, blas_lwork, &work, m, &scratch_vec));
#endif
    PetscCall(VecMDot(b, itg->curl, itg->btilde, itg->last_b_coefs));
    for (j = 0; j < m; ++j) {
      for (i = 0; i < m; ++i) corr[m * j + i] = itg->corr[(itg->maxl) * j + i];
    }
    PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
    PetscReal max_s_value = 0.0;
#if defined(PETSC_USE_COMPLEX)
    PetscCallBLAS("LAPACKheev", LAPACKheev_("V", "L", &blas_m, corr, &blas_m, s_values, work, &blas_lwork, rwork, &blas_info));
#else
    PetscCallBLAS("LAPACKsyev", LAPACKsyev_("V", "L", &blas_m, corr, &blas_m, s_values, work, &blas_lwork, &blas_info));
#endif

    if (blas_info == 0) {
      /* make corr store singular vectors and s_values store singular values */
      for (j = 0; j < m; ++j) {
        if (s_values[j] < 0.0) {
          s_values[j] = PetscAbsReal(s_values[j]);
          for (i = 0; i < m; ++i) corr[m * j + i] *= -1.0;
        }
        max_s_value = PetscMax(max_s_value, s_values[j]);
      }

      /* manually apply the action of the pseudoinverse */
      PetscCallBLAS("BLASgemv", BLASgemv_("T", &blas_m, &blas_m, &one, corr, &blas_m, itg->last_b_coefs, &blas_one, &zero, scratch_vec, &blas_one));
      for (j = 0; j < m; ++j) {
        if (s_values[j] > itg->tol * max_s_value) {
          scratch_vec[j] /= s_values[j];
          blas_rank += 1;
        } else {
          scratch_vec[j] = 0.0;
        }
      }
      PetscCallBLAS("BLASgemv", BLASgemv_("N", &blas_m, &blas_m, &one, corr, &blas_m, scratch_vec, &blas_one, &zero, itg->alpha, &blas_one));

    } else {
      PetscCall(PetscInfo(guess, "Warning eigenvalue solver failed with error code %d - setting initial guess to zero\n", (int)blas_info));
      PetscCall(PetscMemzero(itg->alpha, sizeof(*itg->alpha) * itg->maxl));
    }
    PetscCall(PetscFPTrapPop());

    if (itg->monitor && blas_info == 0) {
      PetscCall(PetscPrintf(((PetscObject)guess)->comm, "KSPFischerGuess correlation rank = %d\n", (int)blas_rank));
      PetscCall(PetscPrintf(((PetscObject)guess)->comm, "KSPFischerGuess singular values = "));
      for (i = 0; i < itg->curl; i++) PetscCall(PetscPrintf(((PetscObject)guess)->comm, " %g", (double)s_values[i]));
      PetscCall(PetscPrintf(((PetscObject)guess)->comm, "\n"));

      PetscCall(PetscPrintf(((PetscObject)guess)->comm, "KSPFischerGuess alphas ="));
      for (i = 0; i < itg->curl; i++) PetscCall(PetscPrintf(((PetscObject)guess)->comm, " %g", (double)PetscAbsScalar(itg->alpha[i])));
      PetscCall(PetscPrintf(((PetscObject)guess)->comm, "\n"));
    }
    /* Form the initial guess by using b's projection coefficients with the xs */
    PetscCall(VecMAXPY(x, itg->curl, itg->alpha, itg->xtilde));
#if defined(PETSC_USE_COMPLEX)
    PetscCall(PetscFree5(corr, s_values, work, rwork, scratch_vec));
#else
    PetscCall(PetscFree4(corr, s_values, work, scratch_vec));
#endif
  }
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode KSPGuessUpdate_Fischer_3(KSPGuess guess, Vec b, Vec x)
{
  KSPGuessFischer *itg    = (KSPGuessFischer *)guess->data;
  PetscBool        rotate = itg->curl == itg->maxl ? PETSC_TRUE : PETSC_FALSE;
  PetscInt         i, j;
  PetscObjectState b_state;
  PetscScalar     *last_column;
  Vec              oldest;

  PetscFunctionBegin;
  if (rotate) {
    /* we have the maximum number of vectors so rotate: oldest vector is at index 0 */
    oldest = itg->xtilde[0];
    for (i = 1; i < itg->curl; ++i) itg->xtilde[i - 1] = itg->xtilde[i];
    itg->xtilde[itg->curl - 1] = oldest;
    PetscCall(VecCopy(x, itg->xtilde[itg->curl - 1]));

    oldest = itg->btilde[0];
    for (i = 1; i < itg->curl; ++i) itg->btilde[i - 1] = itg->btilde[i];
    itg->btilde[itg->curl - 1] = oldest;
    PetscCall(VecCopy(b, itg->btilde[itg->curl - 1]));
    /* shift correlation matrix up and left */
    for (j = 1; j < itg->maxl; ++j) {
      for (i = 1; i < itg->maxl; ++i) itg->corr[(j - 1) * itg->maxl + i - 1] = itg->corr[j * itg->maxl + i];
    }
  } else {
    /* append new vectors */
    PetscCall(VecCopy(x, itg->xtilde[itg->curl]));
    PetscCall(VecCopy(b, itg->btilde[itg->curl]));
    itg->curl++;
  }

  /*
      Populate new column of the correlation matrix and then copy it into the
      row. itg->maxl is the allocated length per column: itg->curl is the actual
      column length.
      If possible reuse the dot products from FormGuess
  */
  last_column = itg->corr + (itg->curl - 1) * itg->maxl;
  PetscCall(PetscObjectStateGet((PetscObject)b, &b_state));
  if (b_state == itg->last_b_state && b == itg->last_b) {
    if (rotate) {
      for (i = 1; i < itg->maxl; ++i) itg->last_b_coefs[i - 1] = itg->last_b_coefs[i];
    }
    PetscCall(VecDot(b, b, &itg->last_b_coefs[itg->curl - 1]));
    PetscCall(PetscArraycpy(last_column, itg->last_b_coefs, itg->curl));
  } else {
    PetscCall(VecMDot(b, itg->curl, itg->btilde, last_column));
  }
  for (i = 0; i < itg->curl; ++i) itg->corr[i * itg->maxl + itg->curl - 1] = last_column[i];
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode KSPGuessSetFromOptions_Fischer(KSPGuess guess)
{
  KSPGuessFischer *ITG  = (KSPGuessFischer *)guess->data;
  PetscInt         nmax = 2, model[2];
  PetscBool        flg;

  PetscFunctionBegin;
  model[0] = ITG->method;
  model[1] = ITG->maxl;
  PetscOptionsBegin(PetscObjectComm((PetscObject)guess), ((PetscObject)guess)->prefix, "Fischer guess options", "KSPGuess");
  PetscCall(PetscOptionsIntArray("-ksp_guess_fischer_model", "Model type and dimension of basis", "KSPGuessFischerSetModel", model, &nmax, &flg));
  if (flg) PetscCall(KSPGuessFischerSetModel(guess, model[0], model[1]));
  PetscCall(PetscOptionsReal("-ksp_guess_fischer_tol", "Tolerance to determine rank via ratio of singular values", "KSPGuessSetTolerance", ITG->tol, &ITG->tol, NULL));
  PetscCall(PetscOptionsBool("-ksp_guess_fischer_monitor", "Monitor the guess", NULL, ITG->monitor, &ITG->monitor, NULL));
  PetscOptionsEnd();
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode KSPGuessSetTolerance_Fischer(KSPGuess guess, PetscReal tol)
{
  KSPGuessFischer *itg = (KSPGuessFischer *)guess->data;

  PetscFunctionBegin;
  itg->tol = tol;
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode KSPGuessView_Fischer(KSPGuess guess, PetscViewer viewer)
{
  KSPGuessFischer *itg = (KSPGuessFischer *)guess->data;
  PetscBool        isascii;

  PetscFunctionBegin;
  PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
  if (isascii) PetscCall(PetscViewerASCIIPrintf(viewer, "Model %" PetscInt_FMT ", size %" PetscInt_FMT "\n", itg->method, itg->maxl));
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
  KSPGuessFischerSetModel - Set the Paul Fischer algorithm or its variants to compute the initial guess for a `KSPSolve()`

  Logically Collective

  Input Parameters:
+ guess - the initial guess context
. model - use model 1, model 2, model 3, or any other number to turn it off
- size  - size of subspace used to generate initial guess

  Options Database Key:
. -ksp_guess_fischer_model <model,size> - uses the Fischer initial guess generator for repeated linear solves

  Level: advanced

.seealso: [](ch_ksp), `KSPGuess`, `KSPGuessCreate()`, `KSPSetUseFischerGuess()`, `KSPSetGuess()`, `KSPGetGuess()`, `KSP`
@*/
PetscErrorCode KSPGuessFischerSetModel(KSPGuess guess, PetscInt model, PetscInt size)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(guess, KSPGUESS_CLASSID, 1);
  PetscValidLogicalCollectiveInt(guess, model, 2);
  PetscTryMethod(guess, "KSPGuessFischerSetModel_C", (KSPGuess, PetscInt, PetscInt), (guess, model, size));
  PetscFunctionReturn(PETSC_SUCCESS);
}

static PetscErrorCode KSPGuessFischerSetModel_Fischer(KSPGuess guess, PetscInt model, PetscInt size)
{
  KSPGuessFischer *itg = (KSPGuessFischer *)guess->data;

  PetscFunctionBegin;
  if (model == 1) {
    guess->ops->update    = KSPGuessUpdate_Fischer_1;
    guess->ops->formguess = KSPGuessFormGuess_Fischer_1;
  } else if (model == 2) {
    guess->ops->update    = KSPGuessUpdate_Fischer_2;
    guess->ops->formguess = KSPGuessFormGuess_Fischer_2;
  } else if (model == 3) {
    guess->ops->update    = KSPGuessUpdate_Fischer_3;
    guess->ops->formguess = KSPGuessFormGuess_Fischer_3;
  } else {
    guess->ops->update    = NULL;
    guess->ops->formguess = NULL;
    itg->method           = 0;
    PetscFunctionReturn(PETSC_SUCCESS);
  }
  if (size != itg->maxl) {
    PetscCall(PetscFree(itg->alpha));
    PetscCall(VecDestroyVecs(itg->maxl, &itg->btilde));
    PetscCall(VecDestroyVecs(itg->maxl, &itg->xtilde));
    PetscCall(VecDestroy(&itg->guess));
    PetscCall(VecDestroy(&itg->Ax));
  }
  itg->method = model;
  itg->maxl   = size;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*MC
    KSPGUESSFISCHER - Implements Paul Fischer's initial guess algorithms {cite}`fischer1998projection`
    and a non-orthogonalizing variant for situations where a linear system is solved repeatedly

    Level: intermediate

    Notes:
    The algorithm is different from Fischer's paper because we do not CHANGE the right hand side of the new
    problem and solve the problem with an initial guess of zero, rather we solve the original problem
    with a nonzero initial guess (this is done so that the linear solver convergence tests are based on
    the original RHS). We use the $xtilde = x - xguess$ as the new direction so that it is not
    mostly orthogonal to the previous solutions.

    These are not intended to be used directly, they are called by `KSPSolve()` automatically with the command
    line options `-ksp_guess_type fischer` `-ksp_guess_fischer_model <int,int>` or programmatically with
.vb
    KSPGetGuess(ksp,&guess);
    KSPGuessSetType(guess,KSPGUESSFISCHER);
    KSPGuessFischerSetModel(guess,model,basis);
    KSPGuessSetTolerance(guess,PETSC_MACHINE_EPSILON);
.ve
    The default tolerance (which is only used in Method 3) is 32*`PETSC_MACHINE_EPSILON`. This value was chosen
    empirically by trying a range of tolerances and picking the one that lowered the solver iteration count the most
    with five vectors.

    Method 2 is only for positive definite matrices, since it uses the energy norm.

    Method 3 is not in the original paper. It is the same as the first two methods except that it
    does not orthogonalize the input vectors or use A at all. This choice is faster but provides a
    less effective initial guess for large (about 10) numbers of stored vectors.

    Developer Note:
    The option `-ksp_fischer_guess <int,int>` is still available for backward compatibility

.seealso: [](ch_ksp), `KSPGuess`, `KSPGuessType`, `KSP`
M*/

PetscErrorCode KSPGuessCreate_Fischer(KSPGuess guess)
{
  KSPGuessFischer *fischer;

  PetscFunctionBegin;
  PetscCall(PetscNew(&fischer));
  fischer->method = 1; /* defaults to method 1 */
  fischer->maxl   = 10;
  fischer->tol    = 32.0 * PETSC_MACHINE_EPSILON;
  guess->data     = fischer;

  guess->ops->setfromoptions = KSPGuessSetFromOptions_Fischer;
  guess->ops->destroy        = KSPGuessDestroy_Fischer;
  guess->ops->settolerance   = KSPGuessSetTolerance_Fischer;
  guess->ops->setup          = KSPGuessSetUp_Fischer;
  guess->ops->view           = KSPGuessView_Fischer;
  guess->ops->reset          = KSPGuessReset_Fischer;
  guess->ops->update         = KSPGuessUpdate_Fischer_1;
  guess->ops->formguess      = KSPGuessFormGuess_Fischer_1;

  PetscCall(PetscObjectComposeFunction((PetscObject)guess, "KSPGuessFischerSetModel_C", KSPGuessFischerSetModel_Fischer));
  PetscFunctionReturn(PETSC_SUCCESS);
}
