static int PSolve(realtype t, N_Vector c, N_Vector fc,
                  N_Vector r, N_Vector z,
                  realtype gamma, realtype delta,
                  int lr, void *user_data)
{
  realtype ***P;
  sunindextype **pivot;
  int jx, jy, igx, igy, iv, ig, *jigx, *jigy, mx, my, ngx;
  sunindextype mp;
  WebData wdata;

  wdata = (WebData) user_data;

  N_VScale(ONE, r, z);

  /* call GSIter for Gauss-Seidel iterations */

  GSIter(gamma, z, wdata->vtemp, wdata);

  /* Do backsolves for inverse of block-diagonal preconditioner factor */
 
  P = wdata->P;
  pivot = wdata->pivot;
  mx = wdata->mx;
  my = wdata->my;
  ngx = wdata->ngx;
  mp = wdata->mp;
  jigx = wdata->jigx;
  jigy = wdata->jigy;

  iv = 0;
  for (jy = 0; jy < my; jy++) {
    igy = jigy[jy];
    for (jx = 0; jx < mx; jx++) {
      igx = jigx[jx];
      ig = igx + igy*ngx;
      denseGETRS(P[ig], mp, pivot[ig], &(N_VGetArrayPointer(z)[iv]));
      iv += mp;
    }
  }

  /* Solve for the quadrature variable */
  N_VGetArrayPointer(z)[NEQ] = N_VGetArrayPointer(r)[NEQ] + gamma*doubleIntgr(z,ISPEC,wdata);

  return(0);
}
static int PSolveB(realtype t, N_Vector c, 
                   N_Vector cB, N_Vector fcB, 
                   N_Vector r, N_Vector z,
                   realtype gamma, realtype delta, 
                   int lr, void *user_data)
{
  realtype ***P;
  sunindextype **pivot;
  int jx, jy, igx, igy, iv, ig, *jigx, *jigy, mx, my, ngx;
  sunindextype mp;
  WebData wdata;

  wdata = (WebData) user_data;

  N_VScale(ONE, r, z);

  /* call GSIter for Gauss-Seidel iterations (same routine but with gamma=-gamma) */

  GSIter(-gamma, z, wdata->vtemp, wdata);

  /* Do backsolves for inverse of block-diagonal preconditioner factor */
 
  P = wdata->P;
  pivot = wdata->pivot;
  mx = wdata->mx;
  my = wdata->my;
  ngx = wdata->ngx;
  mp = wdata->mp;
  jigx = wdata->jigx;
  jigy = wdata->jigy;

  iv = 0;
  for (jy = 0; jy < my; jy++) {
    igy = jigy[jy];
    for (jx = 0; jx < mx; jx++) {
      igx = jigx[jx];
      ig = igx + igy*ngx;
      denseGETRS(P[ig], mp, pivot[ig], &(N_VGetArrayPointer(z)[iv]));
      iv += mp;
    }
  }

  return(0);
}
/*
  This routine applies two inverse preconditioner matrices
  to the vector r, using the interaction-only block-diagonal Jacobian
  with block-grouping, denoted Jr, and Gauss-Seidel applied to the
  diffusion contribution to the Jacobian, denoted Jd.
  It first calls GSIter for a Gauss-Seidel approximation to
  ((I - gamma*Jd)-inverse)*r, and stores the result in z.
  Then it computes ((I - gamma*Jr)-inverse)*z, using LU factors of the
  blocks in P, and pivot information in pivot, and returns the result in z.
*/
static int PSolve(realtype tn, N_Vector c, N_Vector fc,
                  N_Vector r, N_Vector z,
                  realtype gamma, realtype delta,
                  int lr, void *user_data, N_Vector vtemp)
{
    realtype   ***P;
    long int **pivot;
    int jx, jy, igx, igy, iv, ig, *jigx, *jigy, mx, my, ngx;
    long int mp;
    WebData wdata;

    wdata = (WebData) user_data;

    N_VScale(ONE, r, z);

    /* call GSIter for Gauss-Seidel iterations */

    GSIter(gamma, z, vtemp, wdata);

    /* Do backsolves for inverse of block-diagonal preconditioner factor */

    P = wdata->P;
    pivot = wdata->pivot;
    mx = wdata->mx;
    my = wdata->my;
    ngx = wdata->ngx;
    mp = wdata->mp;
    jigx = wdata->jigx;
    jigy = wdata->jigy;

    iv = 0;
    for (jy = 0; jy < my; jy++) {
        igy = jigy[jy];
        for (jx = 0; jx < mx; jx++) {
            igx = jigx[jx];
            ig = igx + igy*ngx;
            denseGETRS(P[ig], mp, pivot[ig], &(NV_DATA_S(z)[iv]));
            iv += mp;
        }
    }

    return(0);
}