/* -----------------------------------------------------------------
 * Programmer(s): Alan C. Hindmarsh and Radu Serban @ LLNL
 * -----------------------------------------------------------------
 * SUNDIALS Copyright Start
 * Copyright (c) 2002-2022, Lawrence Livermore National Security
 * and Southern Methodist University.
 * All rights reserved.
 *
 * See the top-level LICENSE and NOTICE files for details.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 * SUNDIALS Copyright End
 * -----------------------------------------------------------------
 * This is the implementation file for the optional input and output
 * functions for the CVODES solver.
 * ----------------------------------------------------------------- */

#include <stdio.h>
#include <stdlib.h>

#include "cvodes_impl.h"
#include "cvodes_ls_impl.h"
#include "sundials/sundials_types.h"

#define ZERO   RCONST(0.0)
#define HALF   RCONST(0.5)
#define ONE    RCONST(1.0)
#define TWOPT5 RCONST(2.5)

/*
 * =================================================================
 * CVODES optional input functions
 * =================================================================
 */


/*
 * CVodeSetDeltaGammaMaxLSetup
 *
 * Specifies the gamma ratio threshold to signal for a linear solver setup
 */

int CVodeSetDeltaGammaMaxLSetup(void *cvode_mem, realtype dgmax_lsetup)
{
  CVodeMem cv_mem;

  if (cvode_mem == NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetDeltaGammaMaxLSetup",
                   MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  /* Set value or use default */
  if(dgmax_lsetup < ZERO)
    cv_mem->cv_dgmax_lsetup = DGMAX_LSETUP_DEFAULT;
  else
    cv_mem->cv_dgmax_lsetup = dgmax_lsetup;

  return(CV_SUCCESS);
}

/*
 * CVodeSetErrHandlerFn
 *
 * Specifies the error handler function
 */

int CVodeSetErrHandlerFn(void *cvode_mem, CVErrHandlerFn ehfun, void *eh_data)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetErrHandlerFn", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  cv_mem->cv_ehfun = ehfun;
  cv_mem->cv_eh_data = eh_data;

  return(CV_SUCCESS);
}

/*
 * CVodeSetErrFile
 *
 * Specifies the FILE pointer for output (NULL means no messages)
 */

int CVodeSetErrFile(void *cvode_mem, FILE *errfp)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetErrFile", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  cv_mem->cv_errfp = errfp;

  return(CV_SUCCESS);
}

/*
 * CVodeSetUserData
 *
 * Specifies the user data pointer for f
 */

int CVodeSetUserData(void *cvode_mem, void *user_data)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetUserData", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  cv_mem->cv_user_data = user_data;

  return(CV_SUCCESS);
}

/*
 * CVodeSetMonitorFn
 *
 * Specifies the user function to call for monitoring
 * the solution and/or integrator statistics.
 */

int CVodeSetMonitorFn(void *cvode_mem, CVMonitorFn fn)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetMonitorFn", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

#ifdef SUNDIALS_BUILD_WITH_MONITORING
  cv_mem->cv_monitorfun = fn;
  return(CV_SUCCESS);
#else
  cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES", "CVodeSetMonitorFn", "SUNDIALS was not built with monitoring enabled.");
  return(CV_ILL_INPUT);
#endif
}

/*
 * CVodeSetMonitorFrequency
 *
 * Specifies the frequency with which to call the user function.
 */

int CVodeSetMonitorFrequency(void *cvode_mem, long int nst)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetMonitorFrequency", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  if (nst < 0) {
    cvProcessError(NULL, CV_ILL_INPUT, "CVODES", "CVodeSetMonitorFrequency", "step interval must be >= 0\n");
    return(CV_ILL_INPUT);
  }

  cv_mem = (CVodeMem) cvode_mem;

#ifdef SUNDIALS_BUILD_WITH_MONITORING
  cv_mem->cv_monitor_interval = nst;
  return(CV_SUCCESS);
#else
  cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES", "CVodeSetMonitorFrequency", "SUNDIALS was not built with monitoring enabled.");
  return(CV_ILL_INPUT);
#endif
}

/*
 * CVodeSetMaxOrd
 *
 * Specifies the maximum method order
 */

int CVodeSetMaxOrd(void *cvode_mem, int maxord)
{
  CVodeMem cv_mem;
  int qmax_alloc;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetMaxOrd", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if (maxord <= 0) {
    cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES", "CVodeSetMaxOrd", MSGCV_NEG_MAXORD);
    return(CV_ILL_INPUT);
  }

  /* Cannot increase maximum order beyond the value that
     was used when allocating memory */
  qmax_alloc = cv_mem->cv_qmax_alloc;
  qmax_alloc = SUNMIN(qmax_alloc, cv_mem->cv_qmax_allocQ);
  qmax_alloc = SUNMIN(qmax_alloc, cv_mem->cv_qmax_allocS);

  if (maxord > qmax_alloc) {
    cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES", "CVodeSetMaxOrd", MSGCV_BAD_MAXORD);
    return(CV_ILL_INPUT);
  }

  cv_mem->cv_qmax = maxord;

  return(CV_SUCCESS);
}

/*
 * CVodeSetMaxNumSteps
 *
 * Specifies the maximum number of integration steps
 */

int CVodeSetMaxNumSteps(void *cvode_mem, long int mxsteps)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetMaxNumSteps", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  /* Passing mxsteps=0 sets the default. Passing mxsteps<0 disables the test. */
  if (mxsteps == 0)
    cv_mem->cv_mxstep = MXSTEP_DEFAULT;
  else
    cv_mem->cv_mxstep = mxsteps;

  return(CV_SUCCESS);
}

/*
 * CVodeSetMaxHnilWarns
 *
 * Specifies the maximum number of warnings for small h
 */

int CVodeSetMaxHnilWarns(void *cvode_mem, int mxhnil)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetMaxHnilWarns", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  cv_mem->cv_mxhnil = mxhnil;

  return(CV_SUCCESS);
}

/*
 *CVodeSetStabLimDet
 *
 * Turns on/off the stability limit detection algorithm
 */

int CVodeSetStabLimDet(void *cvode_mem, booleantype sldet)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetStabLimDet", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if( sldet && (cv_mem->cv_lmm != CV_BDF) ) {
    cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES", "CVodeSetStabLimDet", MSGCV_SET_SLDET);
    return(CV_ILL_INPUT);
  }

  cv_mem->cv_sldeton = sldet;

  return(CV_SUCCESS);
}

/*
 * CVodeSetInitStep
 *
 * Specifies the initial step size
 */

int CVodeSetInitStep(void *cvode_mem, realtype hin)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetInitStep", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  cv_mem->cv_hin = hin;

  return(CV_SUCCESS);
}

/*
 * CVodeSetMinStep
 *
 * Specifies the minimum step size
 */

int CVodeSetMinStep(void *cvode_mem, realtype hmin)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetMinStep", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if (hmin < ZERO) {
    cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES", "CVodeSetMinStep", MSGCV_NEG_HMIN);
    return(CV_ILL_INPUT);
  }

  /* Passing 0 sets hmin = zero */
  if (hmin == ZERO) {
    cv_mem->cv_hmin = HMIN_DEFAULT;
    return(CV_SUCCESS);
  }

  if (hmin * cv_mem->cv_hmax_inv > ONE) {
    cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES", "CVodeSetMinStep", MSGCV_BAD_HMIN_HMAX);
    return(CV_ILL_INPUT);
  }

  cv_mem->cv_hmin = hmin;

  return(CV_SUCCESS);
}

/*
 * CVodeSetMaxStep
 *
 * Specifies the maximum step size
 */

int CVodeSetMaxStep(void *cvode_mem, realtype hmax)
{
  realtype hmax_inv;
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetMaxStep", MSGCV_NO_MEM);
    return (CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if (hmax < ZERO) {
    cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES", "CVodeSetMaxStep", MSGCV_NEG_HMAX);
    return(CV_ILL_INPUT);
  }

  /* Passing 0 sets hmax = infinity */
  if (hmax == ZERO) {
    cv_mem->cv_hmax_inv = HMAX_INV_DEFAULT;
    return(CV_SUCCESS);
  }

  hmax_inv = ONE/hmax;
  if (hmax_inv * cv_mem->cv_hmin > ONE) {
    cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES", "CVodeSetMaxStep", MSGCV_BAD_HMIN_HMAX);
    return(CV_ILL_INPUT);
  }

  cv_mem->cv_hmax_inv = hmax_inv;

  return(CV_SUCCESS);
}

/*
 * CVodeSetEtaFixedStepBounds
 *
 * Specifies the bounds for retaining the current step size
 */


int CVodeSetEtaFixedStepBounds(void* cvode_mem, realtype eta_min_fx,
                               realtype eta_max_fx)
{
  CVodeMem cv_mem;

  if (cvode_mem == NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetEtaFixedStepBounds",
                   MSGCV_NO_MEM);
    return (CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  /* set allowed value or use default */
  if (eta_min_fx < ZERO || eta_min_fx >= ONE)
    cv_mem->cv_eta_min_fx = ETA_MIN_FX_DEFAULT;
  else
    cv_mem->cv_eta_min_fx = eta_min_fx;

  if (eta_max_fx <= ONE)
    cv_mem->cv_eta_max_fx = ETA_MAX_FX_DEFAULT;
  else
    cv_mem->cv_eta_max_fx = eta_max_fx;

  return(CV_SUCCESS);
}

/*
 * CVodeSetEtaMaxFirstStep
 *
 * Specifies the maximum step size change on the first step
 */

int CVodeSetEtaMaxFirstStep(void* cvode_mem, realtype eta_max_fs)
{
  CVodeMem cv_mem;

  if (cvode_mem == NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetEtaMaxFirstStep",
                   MSGCV_NO_MEM);
    return (CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  /* set allowed value or use default */
  if (eta_max_fs <= ONE)
    cv_mem->cv_eta_max_fs = ETA_MAX_FS_DEFAULT;
  else
    cv_mem->cv_eta_max_fs = eta_max_fs;

  return(CV_SUCCESS);
}

/*
 * CVodeSetEtaMaxEarlyStep
 *
 * Specifies the maximum step size change on steps early in the integration
 * when nst <= small_nst
 */

int CVodeSetEtaMaxEarlyStep(void* cvode_mem, realtype eta_max_es)
{
  CVodeMem cv_mem;

  if (cvode_mem == NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetEtaMaxEarlyStep",
                   MSGCV_NO_MEM);
    return (CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  /* set allowed value or use default */
  if (eta_max_es <= ONE)
    cv_mem->cv_eta_max_es = ETA_MAX_ES_DEFAULT;
  else
    cv_mem->cv_eta_max_es = eta_max_es;

  return(CV_SUCCESS);
}

/*
 * CVodeSetNumStepsEtaMaxEarlyStep
 *
 * Specifies the maximum number of steps for using the early integration change
 * factor
 */

int CVodeSetNumStepsEtaMaxEarlyStep(void* cvode_mem, long int small_nst)
{
  CVodeMem cv_mem;

  if (cvode_mem == NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetNumStepsEtaMaxEarlyStep",
                   MSGCV_NO_MEM);
    return (CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  /* set allowed value or use default */
  if (small_nst < 0)
    cv_mem->cv_small_nst = SMALL_NST_DEFAULT;
  else
    cv_mem->cv_small_nst = small_nst;

  return(CV_SUCCESS);
}

/*
 * CVodeSetEtaMax
 *
 * Specifies the maximum step size change on a general steps (nst > small_nst)
 */

int CVodeSetEtaMax(void* cvode_mem, realtype eta_max_gs)
{
  CVodeMem cv_mem;

  if (cvode_mem == NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetEtaMax",
                   MSGCV_NO_MEM);
    return (CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  /* set allowed value or use default */
  if (eta_max_gs <= ONE)
    cv_mem->cv_eta_max_gs = ETA_MAX_GS_DEFAULT;
  else
    cv_mem->cv_eta_max_gs = eta_max_gs;

  return(CV_SUCCESS);
}

/*
 * CVodeSetEtaMin
 *
 * Specifies the minimum change on a general steps
 */

int CVodeSetEtaMin(void* cvode_mem, realtype eta_min)
{
  CVodeMem cv_mem;

  if (cvode_mem == NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetEtaMin",
                   MSGCV_NO_MEM);
    return (CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  /* set allowed value or use default */
  if (eta_min <= ZERO || eta_min >= ONE)
    cv_mem->cv_eta_min = ETA_MIN_DEFAULT;
  else
    cv_mem->cv_eta_min = eta_min;

  return(CV_SUCCESS);
}

/*
 * CVodeSetEtaMinErrFail
 *
 * Specifies the minimum step size change after an error test failure
 */

int CVodeSetEtaMinErrFail(void* cvode_mem, realtype eta_min_ef)
{
  CVodeMem cv_mem;

  if (cvode_mem == NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetEtaMinErrFail",
                   MSGCV_NO_MEM);
    return (CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  /* set allowed value or use default */
  if (eta_min_ef <= ZERO || eta_min_ef >= ONE)
    cv_mem->cv_eta_min_ef = ETA_MIN_EF_DEFAULT;
  else
    cv_mem->cv_eta_min_ef = eta_min_ef;

  return(CV_SUCCESS);
}

/*
 * CVodeSetEtaMaxErrFail
 *
 * Specifies the maximum step size change after multiple (>= small_nef) error
 * test failures
 */

int CVodeSetEtaMaxErrFail(void* cvode_mem, realtype eta_max_ef)
{
  CVodeMem cv_mem;

  if (cvode_mem == NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetEtaMaxErrFail",
                   MSGCV_NO_MEM);
    return (CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  /* set allowed value or use default */
  if (eta_max_ef <= ZERO || eta_max_ef >= ONE)
    cv_mem->cv_eta_max_ef = ETA_MAX_EF_DEFAULT;
  else
    cv_mem->cv_eta_max_ef = eta_max_ef;

  return(CV_SUCCESS);
}

/*
 * CVodeSetNumFailsEtaMaxErrFail
 *
 * Specifies the maximum number of error test failures necessary to enforce
 * eta_max_ef
 */

int CVodeSetNumFailsEtaMaxErrFail(void* cvode_mem, int small_nef)
{
  CVodeMem cv_mem;

  if (cvode_mem == NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetNumFailsEtaMaxErrFail",
                   MSGCV_NO_MEM);
    return (CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  /* set allowed value or use default */
  if (small_nef < 0)
    cv_mem->cv_small_nef = SMALL_NEF_DEFAULT;
  else
    cv_mem->cv_small_nef = small_nef;

  return(CV_SUCCESS);
}

/*
 * CVodeSetEtaConvFail
 *
 * Specifies the step size change after a nonlinear solver failure
 */

int CVodeSetEtaConvFail(void* cvode_mem, realtype eta_cf)
{
  CVodeMem cv_mem;

  if (cvode_mem == NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetEtaConvFail",
                   MSGCV_NO_MEM);
    return (CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  /* set allowed value or use default */
  if (eta_cf <= ZERO || eta_cf >= ONE)
    cv_mem->cv_eta_cf = ETA_CF_DEFAULT;
  else
    cv_mem->cv_eta_cf = eta_cf;

  return(CV_SUCCESS);
}

/*
 * CVodeSetStopTime
 *
 * Specifies the time beyond which the integration is not to proceed.
 */

int CVodeSetStopTime(void *cvode_mem, realtype tstop)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetStopTime", MSGCV_NO_MEM);
    return (CV_MEM_NULL);
  }
  cv_mem = (CVodeMem) cvode_mem;

  /* If CVode was called at least once, test if tstop is legal
   * (i.e. if it was not already passed).
   * If CVodeSetStopTime is called before the first call to CVode,
   * tstop will be checked in CVode. */
  if (cv_mem->cv_nst > 0) {

    if ( (tstop - cv_mem->cv_tn) * cv_mem->cv_h < ZERO ) {
      cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES", "CVodeSetStopTime", MSGCV_BAD_TSTOP, tstop, cv_mem->cv_tn);
      return(CV_ILL_INPUT);
    }

  }

  cv_mem->cv_tstop = tstop;
  cv_mem->cv_tstopset = SUNTRUE;

  return(CV_SUCCESS);
}

/*
 * CVodeSetMaxErrTestFails
 *
 * Specifies the maximum number of error test failures during one
 * step try.
 */

int CVodeSetMaxErrTestFails(void *cvode_mem, int maxnef)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetMaxErrTestFails", MSGCV_NO_MEM);
    return (CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  cv_mem->cv_maxnef = maxnef;

  return(CV_SUCCESS);
}

/*
 * CVodeSetMaxConvFails
 *
 * Specifies the maximum number of nonlinear convergence failures
 * during one step try.
 */

int CVodeSetMaxConvFails(void *cvode_mem, int maxncf)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetMaxConvFails", MSGCV_NO_MEM);
    return (CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  cv_mem->cv_maxncf = maxncf;

  return(CV_SUCCESS);
}

/*
 * CVodeSetMaxNonlinIters
 *
 * Specifies the maximum number of nonlinear iterations during
 * one solve.
 */

int CVodeSetMaxNonlinIters(void *cvode_mem, int maxcor)
{
  CVodeMem cv_mem;
  booleantype sensi_sim;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES",
                   "CVodeSetMaxNonlinIters", MSGCV_NO_MEM);
    return (CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  /* Are we computing sensitivities with the simultaneous approach? */
  sensi_sim = (cv_mem->cv_sensi && (cv_mem->cv_ism==CV_SIMULTANEOUS));

  if (sensi_sim) {

    /* check that the NLS is non-NULL */
    if (cv_mem->NLSsim == NULL) {
      cvProcessError(NULL, CV_MEM_FAIL, "CVODES",
                      "CVodeSetMaxNonlinIters", MSGCV_MEM_FAIL);
      return(CV_MEM_FAIL);
    }

    return(SUNNonlinSolSetMaxIters(cv_mem->NLSsim, maxcor));

  } else {

    /* check that the NLS is non-NULL */
    if (cv_mem->NLS == NULL) {
      cvProcessError(NULL, CV_MEM_FAIL, "CVODES",
                      "CVodeSetMaxNonlinIters", MSGCV_MEM_FAIL);
      return(CV_MEM_FAIL);
    }

    return(SUNNonlinSolSetMaxIters(cv_mem->NLS, maxcor));
  }

  return(CV_SUCCESS);
}

/*
 * CVodeSetNonlinConvCoef
 *
 * Specifies the coeficient in the nonlinear solver convergence
 * test
 */

int CVodeSetNonlinConvCoef(void *cvode_mem, realtype nlscoef)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetNonlinConvCoef", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  cv_mem->cv_nlscoef = nlscoef;

  return(CV_SUCCESS);
}

/*
 * CVodeSetLSetupFrequency
 *
 * Specifies the frequency for calling the linear solver setup function to
 * recompute the Jacobian matrix and/or preconditioner
 */

int CVodeSetLSetupFrequency(void *cvode_mem, long int msbp)
{
  CVodeMem cv_mem;

  if (cvode_mem == NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetLSetupFrequency",
                   MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  /* check for a valid input */
  if (msbp < 0) {
    cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES", "CVodeSetLSetupFrequency",
                   "A negative setup frequency was provided");
    return(CV_ILL_INPUT);
  }

  /* use default or user provided value */
  cv_mem->cv_msbp = (msbp == 0) ? MSBP_DEFAULT : msbp;

  return(CV_SUCCESS);
}

/*
 * CVodeSetRootDirection
 *
 * Specifies the direction of zero-crossings to be monitored.
 * The default is to monitor both crossings.
 */

int CVodeSetRootDirection(void *cvode_mem, int *rootdir)
{
  CVodeMem cv_mem;
  int i, nrt;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetRootDirection", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  nrt = cv_mem->cv_nrtfn;
  if (nrt==0) {
    cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES", "CVodeSetRootDirection", MSGCV_NO_ROOT);
    return(CV_ILL_INPUT);
  }

  for(i=0; i<nrt; i++) cv_mem->cv_rootdir[i] = rootdir[i];

  return(CV_SUCCESS);
}

/*
 * CVodeSetNoInactiveRootWarn
 *
 * Disables issuing a warning if some root function appears
 * to be identically zero at the beginning of the integration
 */

int CVodeSetNoInactiveRootWarn(void *cvode_mem)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetNoInactiveRootWarn", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  cv_mem->cv_mxgnull = 0;

  return(CV_SUCCESS);
}

/*
 * CVodeSetConstraints
 *
 * Setup for constraint handling feature
 */

int CVodeSetConstraints(void *cvode_mem, N_Vector constraints)
{
  CVodeMem cv_mem;
  realtype temptest;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetConstraints", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  /* If there are no constraints, destroy data structures */
  if (constraints == NULL) {
    if (cv_mem->cv_constraintsMallocDone) {
      N_VDestroy(cv_mem->cv_constraints);
      cv_mem->cv_lrw -= cv_mem->cv_lrw1;
      cv_mem->cv_liw -= cv_mem->cv_liw1;
    }
    cv_mem->cv_constraintsMallocDone = SUNFALSE;
    cv_mem->cv_constraintsSet = SUNFALSE;
    return(CV_SUCCESS);
  }

  /* Test if required vector ops. are defined */

  if (constraints->ops->nvdiv         == NULL ||
      constraints->ops->nvmaxnorm     == NULL ||
      constraints->ops->nvcompare     == NULL ||
      constraints->ops->nvconstrmask  == NULL ||
      constraints->ops->nvminquotient == NULL) {
    cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES", "CVodeSetConstraints", MSGCV_BAD_NVECTOR);
    return(CV_ILL_INPUT);
  }

  /* Check the constraints vector */
  temptest = N_VMaxNorm(constraints);
  if ((temptest > TWOPT5) || (temptest < HALF)) {
    cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES", "CVodeSetConstraints", MSGCV_BAD_CONSTR);
    return(CV_ILL_INPUT);
  }

  if ( !(cv_mem->cv_constraintsMallocDone) ) {
    cv_mem->cv_constraints = N_VClone(constraints);
    cv_mem->cv_lrw += cv_mem->cv_lrw1;
    cv_mem->cv_liw += cv_mem->cv_liw1;
    cv_mem->cv_constraintsMallocDone = SUNTRUE;
  }

  /* Load the constraints vector */
  N_VScale(ONE, constraints, cv_mem->cv_constraints);

  cv_mem->cv_constraintsSet = SUNTRUE;

  return(CV_SUCCESS);
}

/*
 * =================================================================
 * Quadrature optional input functions
 * =================================================================
 */

int CVodeSetQuadErrCon(void *cvode_mem, booleantype errconQ)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetQuadErrCon", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }
  cv_mem = (CVodeMem) cvode_mem;

  cv_mem->cv_errconQ = errconQ;

  return(CV_SUCCESS);
}

/*
 * =================================================================
 * FSA optional input functions
 * =================================================================
 */

int CVodeSetSensDQMethod(void *cvode_mem, int DQtype, realtype DQrhomax)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetSensDQMethod", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if ( (DQtype != CV_CENTERED) && (DQtype != CV_FORWARD) ) {
    cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES", "CVodeSetSensDQMethod", MSGCV_BAD_DQTYPE);
    return(CV_ILL_INPUT);
  }

  if (DQrhomax < ZERO ) {
    cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES", "CVodeSetSensDQMethod", MSGCV_BAD_DQRHO);
    return(CV_ILL_INPUT);
  }

  cv_mem->cv_DQtype = DQtype;
  cv_mem->cv_DQrhomax = DQrhomax;

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

int CVodeSetSensErrCon(void *cvode_mem, booleantype errconS)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetSensErrCon", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }
  cv_mem = (CVodeMem) cvode_mem;

  cv_mem->cv_errconS = errconS;

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

int CVodeSetSensMaxNonlinIters(void *cvode_mem, int maxcorS)
{
  CVodeMem cv_mem;
  booleantype sensi_stg;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES",
                   "CVodeSetSensMaxNonlinIters", MSGCV_NO_MEM);
    return (CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  /* Are we computing sensitivities with a staggered approach? */
  sensi_stg = (cv_mem->cv_sensi && (cv_mem->cv_ism==CV_STAGGERED));

  if (sensi_stg) {

    /* check that the NLS is non-NULL */
    if (cv_mem->NLSstg == NULL) {
      cvProcessError(NULL, CV_MEM_FAIL, "CVODES",
                      "CVodeSetSensMaxNonlinIters", MSGCV_MEM_FAIL);
      return(CV_MEM_FAIL);
    }

    return(SUNNonlinSolSetMaxIters(cv_mem->NLSstg, maxcorS));

  } else {

    /* check that the NLS is non-NULL */
    if (cv_mem->NLSstg1 == NULL) {
      cvProcessError(NULL, CV_MEM_FAIL, "CVODES",
                      "CVodeSetMaxNonlinIters", MSGCV_MEM_FAIL);
      return(CV_MEM_FAIL);
    }

    return(SUNNonlinSolSetMaxIters(cv_mem->NLSstg1, maxcorS));
  }

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

int CVodeSetSensParams(void *cvode_mem, realtype *p, realtype *pbar, int *plist)
{
  CVodeMem cv_mem;
  int is, Ns;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetSensParams", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  /* Was sensitivity initialized? */

  if (cv_mem->cv_SensMallocDone == SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_SENS, "CVODES", "CVodeSetSensParams", MSGCV_NO_SENSI);
    return(CV_NO_SENS);
  }

  Ns = cv_mem->cv_Ns;

  /* Parameters */

  cv_mem->cv_p = p;

  /* pbar */

  if (pbar != NULL)
    for (is=0; is<Ns; is++) {
      if (pbar[is] == ZERO) {
        cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES", "CVodeSetSensParams", MSGCV_BAD_PBAR);
        return(CV_ILL_INPUT);
      }
      cv_mem->cv_pbar[is] = SUNRabs(pbar[is]);
    }
  else
    for (is=0; is<Ns; is++)
      cv_mem->cv_pbar[is] = ONE;

  /* plist */

  if (plist != NULL)
    for (is=0; is<Ns; is++) {
      if ( plist[is] < 0 ) {
        cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES", "CVodeSetSensParams", MSGCV_BAD_PLIST);
        return(CV_ILL_INPUT);
      }
      cv_mem->cv_plist[is] = plist[is];
    }
  else
    for (is=0; is<Ns; is++)
      cv_mem->cv_plist[is] = is;

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

int CVodeSetQuadSensErrCon(void *cvode_mem, booleantype errconQS)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeSetQuadSensErrCon", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }
  cv_mem = (CVodeMem) cvode_mem;

  /* Was sensitivity initialized? */

  if (cv_mem->cv_SensMallocDone == SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_SENS, "CVODES", "CVodeSetQuadSensTolerances", MSGCV_NO_SENSI);
    return(CV_NO_SENS);
  }

  /* Ckeck if quadrature sensitivity was initialized? */

  if (cv_mem->cv_QuadSensMallocDone == SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_QUADSENS, "CVODES", "CVodeSetQuadSensErrCon", MSGCV_NO_QUADSENSI);
    return(CV_NO_QUAD);
  }

  cv_mem->cv_errconQS = errconQS;

  return(CV_SUCCESS);
}

/*
 * =================================================================
 * CVODES optional output functions
 * =================================================================
 */

/*
 * CVodeGetNumSteps
 *
 * Returns the current number of integration steps
 */

int CVodeGetNumSteps(void *cvode_mem, long int *nsteps)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetNumSteps", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  *nsteps = cv_mem->cv_nst;

  return(CV_SUCCESS);
}

/*
 * CVodeGetNumRhsEvals
 *
 * Returns the current number of calls to f
 */

int CVodeGetNumRhsEvals(void *cvode_mem, long int *nfevals)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetNumRhsEvals", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  *nfevals = cv_mem->cv_nfe;

  return(CV_SUCCESS);
}

/*
 * CVodeGetNumLinSolvSetups
 *
 * Returns the current number of calls to the linear solver setup routine
 */

int CVodeGetNumLinSolvSetups(void *cvode_mem, long int *nlinsetups)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetNumLinSolvSetups", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  *nlinsetups = cv_mem->cv_nsetups;

  return(CV_SUCCESS);
}

/*
 * CVodeGetNumErrTestFails
 *
 * Returns the current number of error test failures
 */

int CVodeGetNumErrTestFails(void *cvode_mem, long int *netfails)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetNumErrTestFails", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  *netfails = cv_mem->cv_netf;

  return(CV_SUCCESS);
}

/*
 * CVodeGetLastOrder
 *
 * Returns the order on the last succesful step
 */

int CVodeGetLastOrder(void *cvode_mem, int *qlast)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetLastOrder", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  *qlast = cv_mem->cv_qu;

  return(CV_SUCCESS);
}

/*
 * CVodeGetCurrentOrder
 *
 * Returns the order to be attempted on the next step
 */

int CVodeGetCurrentOrder(void *cvode_mem, int *qcur)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetCurrentOrder", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  *qcur = cv_mem->cv_next_q;

  return(CV_SUCCESS);
}

/*
 * CVodeGetCurrentGamma
 *
 * Returns the value of gamma for the current step.
 */

int CVodeGetCurrentGamma(void *cvode_mem, realtype *gamma)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetCurrentGamma", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  *gamma = cv_mem->cv_gamma;

  return(CV_SUCCESS);
}

/*
 * CVodeGetNumStabLimOrderReds
 *
 * Returns the number of order reductions triggered by the stability
 * limit detection algorithm
 */

int CVodeGetNumStabLimOrderReds(void *cvode_mem, long int *nslred)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetNumStabLimOrderReds", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if (cv_mem->cv_sldeton==SUNFALSE)
    *nslred = 0;
  else
    *nslred = cv_mem->cv_nor;

  return(CV_SUCCESS);
}

/*
 * CVodeGetActualInitStep
 *
 * Returns the step size used on the first step
 */

int CVodeGetActualInitStep(void *cvode_mem, realtype *hinused)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetActualInitStep", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  *hinused = cv_mem->cv_h0u;

  return(CV_SUCCESS);
}

/*
 * CVodeGetLastStep
 *
 * Returns the step size used on the last successful step
 */

int CVodeGetLastStep(void *cvode_mem, realtype *hlast)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetLastStep", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  *hlast = cv_mem->cv_hu;

  return(CV_SUCCESS);
}

/*
 * CVodeGetCurrentStep
 *
 * Returns the step size to be attempted on the next step
 */

int CVodeGetCurrentStep(void *cvode_mem, realtype *hcur)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetCurrentStep", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  *hcur = cv_mem->cv_next_h;

  return(CV_SUCCESS);
}

/*
 * CVodeGetCurrentState
 *
 * Returns the current state vector
 */

int CVodeGetCurrentState(void *cvode_mem, N_Vector *y)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetCurrentState", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  *y = cv_mem->cv_y;

  return(CV_SUCCESS);
}

/*
 * CVodeGetCurrentStateSens
 *
 * Returns the current sensitivity state vector array
 */

int CVodeGetCurrentStateSens(void *cvode_mem, N_Vector **yS)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetCurrentStateSens", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  *yS = cv_mem->cv_yS;

  return(CV_SUCCESS);
}

/*
 * CVodeGetCurrentSensSolveIndex
 *
 * Returns the current index of the sensitivity solve when using
 * the staggered1 nonlinear solver.
 */

int CVodeGetCurrentSensSolveIndex(void *cvode_mem, int *index)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetCurrentSensSolveIndex", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  *index = cv_mem->sens_solve_idx;

  return(CV_SUCCESS);
}

/*
 * CVodeGetCurrentTime
 *
 * Returns the current value of the independent variable
 */

int CVodeGetCurrentTime(void *cvode_mem, realtype *tcur)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetCurrentTime", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  *tcur = cv_mem->cv_tn;

  return(CV_SUCCESS);
}

/*
 * CVodeGetTolScaleFactor
 *
 * Returns a suggested factor for scaling tolerances
 */

int CVodeGetTolScaleFactor(void *cvode_mem, realtype *tolsfact)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetTolScaleFactor", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  *tolsfact = cv_mem->cv_tolsf;

  return(CV_SUCCESS);
}

/*
 * CVodeGetErrWeights
 *
 * This routine returns the current weight vector.
 */

int CVodeGetErrWeights(void *cvode_mem, N_Vector eweight)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetErrWeights", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  N_VScale(ONE, cv_mem->cv_ewt, eweight);

  return(CV_SUCCESS);
}

/*
 * CVodeGetEstLocalErrors
 *
 * Returns an estimate of the local error
 */

int CVodeGetEstLocalErrors(void *cvode_mem, N_Vector ele)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetEstLocalErrors", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  N_VScale(ONE, cv_mem->cv_acor, ele);

  return(CV_SUCCESS);
}

/*
 * CVodeGetWorkSpace
 *
 * Returns integrator work space requirements
 */

int CVodeGetWorkSpace(void *cvode_mem, long int *lenrw, long int *leniw)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetWorkSpace", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  *leniw = cv_mem->cv_liw;
  *lenrw = cv_mem->cv_lrw;

  return(CV_SUCCESS);
}

/*
 * CVodeGetIntegratorStats
 *
 * Returns integrator statistics
 */

int CVodeGetIntegratorStats(void *cvode_mem, long int *nsteps, long int *nfevals,
                            long int *nlinsetups, long int *netfails, int *qlast,
                            int *qcur, realtype *hinused, realtype *hlast,
                            realtype *hcur, realtype *tcur)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetIntegratorStats", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  *nsteps     = cv_mem->cv_nst;
  *nfevals    = cv_mem->cv_nfe;
  *nlinsetups = cv_mem->cv_nsetups;
  *netfails   = cv_mem->cv_netf;
  *qlast      = cv_mem->cv_qu;
  *qcur       = cv_mem->cv_next_q;
  *hinused    = cv_mem->cv_h0u;
  *hlast      = cv_mem->cv_hu;
  *hcur       = cv_mem->cv_next_h;
  *tcur       = cv_mem->cv_tn;

  return(CV_SUCCESS);
}

/*
 * CVodeGetNumGEvals
 *
 * Returns the current number of calls to g (for rootfinding)
 */

int CVodeGetNumGEvals(void *cvode_mem, long int *ngevals)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetNumGEvals", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  *ngevals = cv_mem->cv_nge;

  return(CV_SUCCESS);
}

/*
 * CVodeGetRootInfo
 *
 * Returns pointer to array rootsfound showing roots found
 */

int CVodeGetRootInfo(void *cvode_mem, int *rootsfound)
{
  CVodeMem cv_mem;
  int i, nrt;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetRootInfo", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  nrt = cv_mem->cv_nrtfn;

  for (i=0; i<nrt; i++) rootsfound[i] = cv_mem->cv_iroots[i];

  return(CV_SUCCESS);
}


/*
 * CVodeGetNumNonlinSolvIters
 *
 * Returns the current number of iterations in the nonlinear solver
 */

int CVodeGetNumNonlinSolvIters(void *cvode_mem, long int *nniters)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetNumNonlinSolvIters",
                   MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  *nniters = cv_mem->cv_nni;

  return(CV_SUCCESS);
}

/*
 * CVodeGetNumNonlinSolvConvFails
 *
 * Returns the current number of convergence failures in the
 * nonlinear solver
 */

int CVodeGetNumNonlinSolvConvFails(void *cvode_mem, long int *nnfails)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetNumNonlinSolvConvFails", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  *nnfails = cv_mem->cv_nnf;

  return(CV_SUCCESS);
}

/*
 * CVodeGetNonlinSolvStats
 *
 * Returns nonlinear solver statistics
 */

int CVodeGetNonlinSolvStats(void *cvode_mem, long int *nniters,
                            long int *nnfails)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetNonlinSolvStats",
                   MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  *nniters = cv_mem->cv_nni;
  *nnfails = cv_mem->cv_nnf;

  return(CV_SUCCESS);
}

/*
 * CVodeGetNumStepSolveFails
 *
 * Returns the current number of failed steps due to a nonlinear solver
 * convergence failure
 */

int CVodeGetNumStepSolveFails(void *cvode_mem, long int *nncfails)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetNumStepSolveFails",
                   MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  *nncfails = cv_mem->cv_ncfn;

  return(CV_SUCCESS);
}

/*
 * =================================================================
 * Quadrature optional output functions
 * =================================================================
 */

/*-----------------------------------------------------------------*/

int CVodeGetQuadNumRhsEvals(void *cvode_mem, long int *nfQevals)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetQuadNumRhsEvals", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if (cv_mem->cv_quadr==SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_QUAD, "CVODES", "CVodeGetQuadNumRhsEvals", MSGCV_NO_QUAD);
    return(CV_NO_QUAD);
  }

  *nfQevals = cv_mem->cv_nfQe;

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

int CVodeGetQuadNumErrTestFails(void *cvode_mem, long int *nQetfails)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetQuadNumErrTestFails", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if (cv_mem->cv_quadr==SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_QUAD, "CVODES", "CVodeGetQuadNumErrTestFails", MSGCV_NO_QUAD);
    return(CV_NO_QUAD);
  }

  *nQetfails = cv_mem->cv_netfQ;

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

int CVodeGetQuadErrWeights(void *cvode_mem, N_Vector eQweight)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetQuadErrWeights", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if (cv_mem->cv_quadr==SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_QUAD, "CVODES", "CVodeGetQuadErrWeights", MSGCV_NO_QUAD);
    return(CV_NO_QUAD);
  }

  if(cv_mem->cv_errconQ) N_VScale(ONE, cv_mem->cv_ewtQ, eQweight);

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

int CVodeGetQuadStats(void *cvode_mem, long int *nfQevals, long int *nQetfails)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetQuadStats", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if (cv_mem->cv_quadr==SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_QUAD, "CVODES", "CVodeGetQuadStats", MSGCV_NO_QUAD);
    return(CV_NO_QUAD);
  }

  *nfQevals = cv_mem->cv_nfQe;
  *nQetfails = cv_mem->cv_netfQ;

  return(CV_SUCCESS);
}

/*
 * =================================================================
 * Quadrature FSA optional output functions
 * =================================================================
 */

/*-----------------------------------------------------------------*/

int CVodeGetQuadSensNumRhsEvals(void *cvode_mem, long int *nfQSevals)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetQuadSensNumRhsEvals", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if (cv_mem->cv_quadr_sensi == SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_QUADSENS, "CVODES", "CVodeGetQuadSensNumRhsEvals", MSGCV_NO_QUADSENSI);
    return(CV_NO_QUADSENS);
  }

  *nfQSevals = cv_mem->cv_nfQSe;

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

int CVodeGetQuadSensNumErrTestFails(void *cvode_mem, long int *nQSetfails)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetQuadSensNumErrTestFails", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if (cv_mem->cv_quadr_sensi == SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_QUADSENS, "CVODES", "CVodeGetQuadSensNumErrTestFails", MSGCV_NO_QUADSENSI);
    return(CV_NO_QUADSENS);
  }

  *nQSetfails = cv_mem->cv_netfQS;

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

int CVodeGetQuadSensErrWeights(void *cvode_mem, N_Vector *eQSweight)
{
  CVodeMem cv_mem;
  int is, Ns;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetQuadSensErrWeights", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if (cv_mem->cv_quadr_sensi == SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_QUADSENS, "CVODES", "CVodeGetQuadSensErrWeights", MSGCV_NO_QUADSENSI);
    return(CV_NO_QUADSENS);
  }
  Ns = cv_mem->cv_Ns;

  if (cv_mem->cv_errconQS)
    for (is=0; is<Ns; is++)
      N_VScale(ONE, cv_mem->cv_ewtQS[is], eQSweight[is]);

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

int CVodeGetQuadSensStats(void *cvode_mem, long int *nfQSevals, long int *nQSetfails)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetQuadSensStats", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if (cv_mem->cv_quadr_sensi == SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_QUADSENS, "CVODES", "CVodeGetQuadSensStats", MSGCV_NO_QUADSENSI);
    return(CV_NO_QUADSENS);
  }

  *nfQSevals = cv_mem->cv_nfQSe;
  *nQSetfails = cv_mem->cv_netfQS;

  return(CV_SUCCESS);
}


/*
 * =================================================================
 * FSA optional output functions
 * =================================================================
 */

/*-----------------------------------------------------------------*/

int CVodeGetSensNumRhsEvals(void *cvode_mem, long int *nfSevals)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetSensNumRhsEvals", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if (cv_mem->cv_sensi==SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_SENS, "CVODES", "CVodeGetSensNumRhsEvals", MSGCV_NO_SENSI);
    return(CV_NO_SENS);
  }

  *nfSevals = cv_mem->cv_nfSe;

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

int CVodeGetNumRhsEvalsSens(void *cvode_mem, long int *nfevalsS)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetNumRhsEvalsSens", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if (cv_mem->cv_sensi==SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_SENS, "CVODES", "CVodeGetNumRhsEvalsSens", MSGCV_NO_SENSI);
    return(CV_NO_SENS);
  }

  *nfevalsS = cv_mem->cv_nfeS;

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

int CVodeGetSensNumErrTestFails(void *cvode_mem, long int *nSetfails)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetSensNumErrTestFails", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if (cv_mem->cv_sensi==SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_SENS, "CVODES", "CVodeGetSensNumErrTestFails", MSGCV_NO_SENSI);
    return(CV_NO_SENS);
  }

  *nSetfails = cv_mem->cv_netfS;

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

int CVodeGetSensNumLinSolvSetups(void *cvode_mem, long int *nlinsetupsS)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetSensNumLinSolvSetups", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if (cv_mem->cv_sensi==SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_SENS, "CVODES", "CVodeGetSensNumLinSolvSetups", MSGCV_NO_SENSI);
    return(CV_NO_SENS);
  }

  *nlinsetupsS = cv_mem->cv_nsetupsS;

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

int CVodeGetSensErrWeights(void *cvode_mem, N_Vector *eSweight)
{
  CVodeMem cv_mem;
  int is, Ns;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetSensErrWeights", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if (cv_mem->cv_sensi==SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_SENS, "CVODES", "CVodeGetSensErrWeights", MSGCV_NO_SENSI);
    return(CV_NO_SENS);
  }

  Ns = cv_mem->cv_Ns;

  for (is=0; is<Ns; is++)
    N_VScale(ONE, cv_mem->cv_ewtS[is], eSweight[is]);

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

int CVodeGetSensStats(void *cvode_mem, long int *nfSevals, long int *nfevalsS,
                      long int *nSetfails, long int *nlinsetupsS)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetSensStats", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if (cv_mem->cv_sensi==SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_SENS, "CVODES", "CVodeGetSensStats", MSGCV_NO_SENSI);
    return(CV_NO_SENS);
  }

  *nfSevals = cv_mem->cv_nfSe;
  *nfevalsS = cv_mem->cv_nfeS;
  *nSetfails = cv_mem->cv_netfS;
  *nlinsetupsS = cv_mem->cv_nsetupsS;

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

int CVodeGetSensNumNonlinSolvIters(void *cvode_mem, long int *nSniters)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES",
                   "CVodeGetSensNumNonlinSolvIters", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if (cv_mem->cv_sensi==SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_SENS, "CVODES",
                   "CVodeGetSensNumNonlinSolvIters", MSGCV_NO_SENSI);
    return(CV_NO_SENS);
  }

  *nSniters = cv_mem->cv_nniS;

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

int CVodeGetSensNumNonlinSolvConvFails(void *cvode_mem, long int *nSnfails)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetSensNumNonlinSolvConvFails", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if (cv_mem->cv_sensi==SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_SENS, "CVODES", "CVodeGetSensNumNonlinSolvConvFails", MSGCV_NO_SENSI);
    return(CV_NO_SENS);
  }

  *nSnfails = cv_mem->cv_nnfS;

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

int CVodeGetSensNonlinSolvStats(void *cvode_mem, long int *nSniters,
                                long int *nSnfails)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES",
                   "CVodeGetSensNonlinSolvStats", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if (cv_mem->cv_sensi==SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_SENS, "CVODES",
                   "CVodeGetSensNonlinSolvStats", MSGCV_NO_SENSI);
    return(CV_NO_SENS);
  }

  *nSniters = cv_mem->cv_nniS;
  *nSnfails = cv_mem->cv_nnfS;

  return(CV_SUCCESS);
}

int CVodeGetNumStepSensSolveFails(void *cvode_mem, long int *nSncfails)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetNumStepSensSolveFails",
                   MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  if (cv_mem->cv_sensi==SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_SENS, "CVODES",
                   "CVodeGetNumStepSensSolveFails", MSGCV_NO_SENSI);
    return(CV_NO_SENS);
  }

  *nSncfails = cv_mem->cv_ncfnS;

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

int CVodeGetStgrSensNumNonlinSolvIters(void *cvode_mem, long int *nSTGR1niters)
{
  CVodeMem cv_mem;
  int is, Ns;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetStgrSensNumNonlinSolvIters", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  Ns = cv_mem->cv_Ns;

  if (cv_mem->cv_sensi==SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_SENS, "CVODES", "CVodeGetStgrSensNumNonlinSolvIters", MSGCV_NO_SENSI);
    return(CV_NO_SENS);
  }

  if(cv_mem->cv_ism==CV_STAGGERED1)
    for(is=0; is<Ns; is++) nSTGR1niters[is] = cv_mem->cv_nniS1[is];

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

int CVodeGetStgrSensNumNonlinSolvConvFails(void *cvode_mem, long int *nSTGR1nfails)
{
  CVodeMem cv_mem;
  int is, Ns;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetStgrSensNumNonlinSolvConvFails", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  Ns = cv_mem->cv_Ns;

  if (cv_mem->cv_sensi==SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_SENS, "CVODES", "CVodeGetStgrSensNumNonlinSolvConvFails", MSGCV_NO_SENSI);
    return(CV_NO_SENS);
  }

  if(cv_mem->cv_ism==CV_STAGGERED1)
    for(is=0; is<Ns; is++) nSTGR1nfails[is] = cv_mem->cv_nnfS1[is];

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

int CVodeGetStgrSensNonlinSolvStats(void *cvode_mem,
                                    long int *nSTGR1niters,
                                    long int *nSTGR1nfails)
{
  CVodeMem cv_mem;
  int is, Ns;

  if (cvode_mem == NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES",
                   "CVodeGetStgrSensNonlinSolvStats", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  Ns = cv_mem->cv_Ns;

  if (cv_mem->cv_sensi == SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_SENS, "CVODES",
                   "CVodeGetStgrSensNonlinSolvStats", MSGCV_NO_SENSI);
    return(CV_NO_SENS);
  }

  if(cv_mem->cv_ism == CV_STAGGERED1) {
    for(is=0; is<Ns; is++) nSTGR1niters[is] = cv_mem->cv_nniS1[is];
    for(is=0; is<Ns; is++) nSTGR1nfails[is] = cv_mem->cv_nnfS1[is];
  }

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

int CVodeGetNumStepStgrSensSolveFails(void *cvode_mem, long int *nSTGR1ncfails)
{
  CVodeMem cv_mem;
  int is, Ns;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES",
                   "CVodeGetNumStepStgrSensSolveFails", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  Ns = cv_mem->cv_Ns;

  if (cv_mem->cv_sensi==SUNFALSE) {
    cvProcessError(cv_mem, CV_NO_SENS, "CVODES",
                   "CVodeGetNumStepStgrSensSolveFails", MSGCV_NO_SENSI);
    return(CV_NO_SENS);
  }

  if(cv_mem->cv_ism==CV_STAGGERED1)
    for(is=0; is<Ns; is++) nSTGR1ncfails[is] = cv_mem->cv_ncfnS1[is];

  return(CV_SUCCESS);
}

/*
 * CVodePrintAllStats
 *
 * Print all integrator statistics
 */

int CVodePrintAllStats(void *cvode_mem, FILE *outfile, SUNOutputFormat fmt)
{
  CVodeMem cv_mem;
  CVLsMem cvls_mem;
  CVodeProjMem cvproj_mem;
  int is;

  if (cvode_mem == NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodePrintAllStats",
                   MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  switch(fmt)
  {
  case SUN_OUTPUTFORMAT_TABLE:
    /* step and method stats */
    fprintf(outfile, "Current time                 = %"RSYM"\n", cv_mem->cv_tn);
    fprintf(outfile, "Steps                        = %ld\n", cv_mem->cv_nst);
    fprintf(outfile, "Error test fails             = %ld\n", cv_mem->cv_netf);
    fprintf(outfile, "NLS step fails               = %ld\n", cv_mem->cv_ncfn);
    fprintf(outfile, "Initial step size            = %"RSYM"\n", cv_mem->cv_h0u);
    fprintf(outfile, "Last step size               = %"RSYM"\n", cv_mem->cv_hu);
    fprintf(outfile, "Current step size            = %"RSYM"\n", cv_mem->cv_next_h);
    fprintf(outfile, "Last method order            = %d\n", cv_mem->cv_qu);
    fprintf(outfile, "Current method order         = %d\n", cv_mem->cv_next_q);
    fprintf(outfile, "Stab. lim. order reductions  = %ld\n", cv_mem->cv_nor);

    /* function evaluations */
    fprintf(outfile, "RHS fn evals                 = %ld\n", cv_mem->cv_nfe);

    /* nonlinear solver stats */
    fprintf(outfile, "NLS iters                    = %ld\n", cv_mem->cv_nni);
    fprintf(outfile, "NLS fails                    = %ld\n", cv_mem->cv_nnf);
    if (cv_mem->cv_nst > 0)
    {
      fprintf(outfile, "NLS iters per step           = %"RSYM"\n",
              (realtype) cv_mem->cv_nni / (realtype) cv_mem->cv_nst);
    }

    /* linear solver stats */
    fprintf(outfile, "LS setups                    = %ld\n", cv_mem->cv_nsetups);
    if (cv_mem->cv_lmem)
    {
      cvls_mem = (CVLsMem) (cv_mem->cv_lmem);
      fprintf(outfile, "Jac fn evals                 = %ld\n", cvls_mem->nje);
      fprintf(outfile, "LS RHS fn evals              = %ld\n", cvls_mem->nfeDQ);
      fprintf(outfile, "Prec setup evals             = %ld\n", cvls_mem->npe);
      fprintf(outfile, "Prec solves                  = %ld\n", cvls_mem->nps);
      fprintf(outfile, "LS iters                     = %ld\n", cvls_mem->nli);
      fprintf(outfile, "LS fails                     = %ld\n", cvls_mem->ncfl);
      fprintf(outfile, "Jac-times setups             = %ld\n", cvls_mem->njtsetup);
      fprintf(outfile, "Jac-times evals              = %ld\n", cvls_mem->njtimes);
      if (cv_mem->cv_nni > 0)
      {
        fprintf(outfile, "LS iters per NLS iter        = %"RSYM"\n",
                (realtype) cvls_mem->nli / (realtype) cv_mem->cv_nni);
        fprintf(outfile, "Jac evals per NLS iter       = %"RSYM"\n",
                (realtype) cvls_mem->nje / (realtype) cv_mem->cv_nni);
        fprintf(outfile, "Prec evals per NLS iter      = %"RSYM"\n",
                (realtype) cvls_mem->npe / (realtype) cv_mem->cv_nni);
      }
    }

    /* rootfinding stats */
    fprintf(outfile, "Root fn evals                = %ld\n", cv_mem->cv_nge);

    /* projection stats */
    if (cv_mem->proj_mem)
    {
      cvproj_mem = (CVodeProjMem) (cv_mem->proj_mem);
      fprintf(outfile, "Projection fn evals          = %ld\n", cvproj_mem->nproj);
      fprintf(outfile, "Projection fails             = %ld\n", cvproj_mem->npfails);
    }

    /* quadrature stats */
    if (cv_mem->cv_quadr)
    {
      fprintf(outfile, "Quad fn evals                = %ld\n", cv_mem->cv_nfQe);
      fprintf(outfile, "Quad error test fails        = %ld\n", cv_mem->cv_netfQ);
    }

    /* sensitivity stats */
    if (cv_mem->cv_sensi)
    {
      fprintf(outfile, "Sens fn evals                = %ld\n", cv_mem->cv_nfSe);
      fprintf(outfile, "Sens RHS fn evals            = %ld\n", cv_mem->cv_nfeS);
      fprintf(outfile, "Sens error test fails        = %ld\n", cv_mem->cv_netfS);
      if (cv_mem->cv_ism != CV_SIMULTANEOUS)
      {
        fprintf(outfile, "Sens NLS iters               = %ld\n", cv_mem->cv_nniS);
        fprintf(outfile, "Sens NLS fails               = %ld\n", cv_mem->cv_nnfS);
        fprintf(outfile, "Sens NLS step fails          = %ld\n", cv_mem->cv_ncfnS);
      }
      if (cv_mem->cv_ism == CV_STAGGERED1)
      {
        fprintf(outfile, "Sens stgr1 NLS iters         = %ld", cv_mem->cv_nniS1[0]);
        for (is = 1; is < cv_mem->cv_Ns; is++)
          fprintf(outfile, ", %ld", cv_mem->cv_nniS1[is]);
        fprintf(outfile, "\n");
        fprintf(outfile, "Sens stgr1 NLS fails         = %ld", cv_mem->cv_nnfS1[0]);
        for(is = 1; is < cv_mem->cv_Ns; is++)
          fprintf(outfile, ", %ld", cv_mem->cv_nnfS1[is]);
        fprintf(outfile, "\n");
        fprintf(outfile, "Sens stgr1 NLS step fails    = %ld", cv_mem->cv_ncfnS1[0]);
        for(is = 1; is < cv_mem->cv_Ns; is++)
          fprintf(outfile, ", %ld", cv_mem->cv_ncfnS1[is]);
        fprintf(outfile, "\n");
      }
      fprintf(outfile, "Sens LS setups               = %ld\n", cv_mem->cv_nsetupsS);
    }

    /* quadrature-sensitivity stats */
    if (cv_mem->cv_quadr_sensi)
    {
      fprintf(outfile, "QuadSens fn evals            = %ld\n", cv_mem->cv_nfQSe);
      fprintf(outfile, "QuadSens error test fails    = %ld\n", cv_mem->cv_netfQS);
    }
    break;

  case SUN_OUTPUTFORMAT_CSV:
    /* step and method stats */
    fprintf(outfile, "Time,%"RSYM, cv_mem->cv_tn);
    fprintf(outfile, ",Steps,%ld", cv_mem->cv_nst);
    fprintf(outfile, ",Error test fails,%ld", cv_mem->cv_netf);
    fprintf(outfile, ",NLS step fails,%ld", cv_mem->cv_ncfn);
    fprintf(outfile, ",Initial step size,%"RSYM, cv_mem->cv_h0u);
    fprintf(outfile, ",Last step size,%"RSYM, cv_mem->cv_hu);
    fprintf(outfile, ",Current step size,%"RSYM, cv_mem->cv_next_h);
    fprintf(outfile, ",Last method order,%d", cv_mem->cv_qu);
    fprintf(outfile, ",Current method order,%d", cv_mem->cv_next_q);
    fprintf(outfile, ",Stab. lim. order reductions,%ld", cv_mem->cv_nor);

    /* function evaluations */
    fprintf(outfile, ",RHS fn evals,%ld", cv_mem->cv_nfe);

    /* nonlinear solver stats */
    fprintf(outfile, ",NLS iters,%ld", cv_mem->cv_nni);
    fprintf(outfile, ",NLS fails,%ld", cv_mem->cv_nnf);
    if (cv_mem->cv_nst > 0)
    {
      fprintf(outfile, ",NLS iters per step,%"RSYM,
              (realtype) cv_mem->cv_nni / (realtype) cv_mem->cv_nst);
    }
    else
    {
      fprintf(outfile, ",NLS iters per step,0");
    }

    /* linear solver stats */
    fprintf(outfile, ",LS setups,%ld", cv_mem->cv_nsetups);
    if (cv_mem->cv_lmem)
    {
      cvls_mem = (CVLsMem) (cv_mem->cv_lmem);
      fprintf(outfile, ",Jac fn evals,%ld", cvls_mem->nje);
      fprintf(outfile, ",LS RHS fn evals,%ld", cvls_mem->nfeDQ);
      fprintf(outfile, ",Prec setup evals,%ld", cvls_mem->npe);
      fprintf(outfile, ",Prec solves,%ld", cvls_mem->nps);
      fprintf(outfile, ",LS iters,%ld", cvls_mem->nli);
      fprintf(outfile, ",LS fails,%ld", cvls_mem->ncfl);
      fprintf(outfile, ",Jac-times setups,%ld", cvls_mem->njtsetup);
      fprintf(outfile, ",Jac-times evals,%ld", cvls_mem->njtimes);
      if (cv_mem->cv_nni > 0)
      {
        fprintf(outfile, ",LS iters per NLS iter,%"RSYM,
                (realtype) cvls_mem->nli / (realtype) cv_mem->cv_nni);
        fprintf(outfile, ",Jac evals per NLS iter,%"RSYM,
                (realtype) cvls_mem->nje / (realtype) cv_mem->cv_nni);
        fprintf(outfile, ",Prec evals per NLS iter,%"RSYM,
                (realtype) cvls_mem->npe / (realtype) cv_mem->cv_nni);
      }
      else
      {
        fprintf(outfile, ",LS iters per NLS iter,0");
        fprintf(outfile, ",Jac evals per NLS iter,0");
        fprintf(outfile, ",Prec evals per NLS iter,0");
      }
    }

    /* rootfinding stats */
    fprintf(outfile, ",Root fn evals,%ld", cv_mem->cv_nge);

    /* projection stats */
    if (cv_mem->proj_mem)
    {
      cvproj_mem = (CVodeProjMem) (cv_mem->proj_mem);
      fprintf(outfile, ",Projection fn evals,%ld", cvproj_mem->nproj);
      fprintf(outfile, ",Projection fails,%ld", cvproj_mem->npfails);
    }

    /* quadrature stats */
    if (cv_mem->cv_quadr)
    {
      fprintf(outfile, ",Quad fn evals,%ld", cv_mem->cv_nfQe);
      fprintf(outfile, ",Quad error test fails,%ld", cv_mem->cv_netfQ);
    }

    /* sensitivity stats */
    if (cv_mem->cv_sensi)
    {
      fprintf(outfile, ",Sens fn evals,%ld", cv_mem->cv_nfSe);
      fprintf(outfile, ",Sens RHS fn evals,%ld", cv_mem->cv_nfeS);
      fprintf(outfile, ",Sens error test fails,%ld", cv_mem->cv_netfS);
      if (cv_mem->cv_ism != CV_SIMULTANEOUS)
      {
        fprintf(outfile, ",Sens NLS iters,%ld", cv_mem->cv_nniS);
        fprintf(outfile, ",Sens NLS fails,%ld", cv_mem->cv_nnfS);
        fprintf(outfile, ",Sens NLS step fails,%ld", cv_mem->cv_ncfnS);
      }
      if (cv_mem->cv_ism == CV_STAGGERED1)
      {
        for (is = 0; is < cv_mem->cv_Ns; is++)
          fprintf(outfile, ",Sens stgr1[%i] NLS iters,%ld",
                  is, cv_mem->cv_nniS1[is]);
        for (is = 0; is < cv_mem->cv_Ns; is++)
          fprintf(outfile, ",Sens stgr1[%i] NLS fails,%ld",
                  is, cv_mem->cv_nnfS1[is]);
        for (is = 0; is < cv_mem->cv_Ns; is++)
          fprintf(outfile, ",Sens stgr1[%i] NLS step fails,%ld",
                  is, cv_mem->cv_ncfnS1[is]);
      }
      fprintf(outfile, ",Sens LS setups,%ld", cv_mem->cv_nsetupsS);
    }

    /* quadrature-sensitivity stats */
    if (cv_mem->cv_quadr_sensi)
    {
      fprintf(outfile, ",QuadSens fn evals,%ld", cv_mem->cv_nfQSe);
      fprintf(outfile, ",QuadSens error test fails,%ld", cv_mem->cv_netfQS);
    }
    fprintf(outfile, "\n");
    break;

  default:
    cvProcessError(cv_mem, CV_ILL_INPUT, "CVODES", "CVodePrintAllStats",
                   "Invalid formatting option.");
    return(CV_ILL_INPUT);
  }

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

int CVodeGetUserData(void *cvode_mem, void** user_data)
{
  CVodeMem cv_mem;

  if (cvode_mem==NULL) {
    cvProcessError(NULL, CV_MEM_NULL, "CVODES", "CVodeGetUserData", MSGCV_NO_MEM);
    return(CV_MEM_NULL);
  }

  cv_mem = (CVodeMem) cvode_mem;

  *user_data = cv_mem->cv_user_data;

  return(CV_SUCCESS);
}

/*-----------------------------------------------------------------*/

char *CVodeGetReturnFlagName(long int flag)
{
  char *name;

  name = (char *)malloc(24*sizeof(char));

  switch(flag) {
  case CV_SUCCESS:
    sprintf(name,"CV_SUCCESS");
    break;
  case CV_TSTOP_RETURN:
    sprintf(name,"CV_TSTOP_RETURN");
    break;
  case CV_ROOT_RETURN:
    sprintf(name,"CV_ROOT_RETURN");
    break;
  case CV_TOO_MUCH_WORK:
    sprintf(name,"CV_TOO_MUCH_WORK");
    break;
  case CV_TOO_MUCH_ACC:
    sprintf(name,"CV_TOO_MUCH_ACC");
    break;
  case CV_ERR_FAILURE:
    sprintf(name,"CV_ERR_FAILURE");
    break;
  case CV_CONV_FAILURE:
    sprintf(name,"CV_CONV_FAILURE");
    break;
  case CV_LINIT_FAIL:
    sprintf(name,"CV_LINIT_FAIL");
    break;
  case CV_LSETUP_FAIL:
    sprintf(name,"CV_LSETUP_FAIL");
    break;
  case CV_LSOLVE_FAIL:
    sprintf(name,"CV_LSOLVE_FAIL");
    break;
  case CV_RHSFUNC_FAIL:
    sprintf(name,"CV_RHSFUNC_FAIL");
    break;
  case CV_FIRST_RHSFUNC_ERR:
    sprintf(name,"CV_FIRST_RHSFUNC_ERR");
    break;
  case CV_REPTD_RHSFUNC_ERR:
    sprintf(name,"CV_REPTD_RHSFUNC_ERR");
    break;
  case CV_UNREC_RHSFUNC_ERR:
    sprintf(name,"CV_UNREC_RHSFUNC_ERR");
    break;
  case CV_RTFUNC_FAIL:
    sprintf(name,"CV_RTFUNC_FAIL");
    break;
  case CV_MEM_FAIL:
    sprintf(name,"CV_MEM_FAIL");
    break;
  case CV_MEM_NULL:
    sprintf(name,"CV_MEM_NULL");
    break;
  case CV_ILL_INPUT:
    sprintf(name,"CV_ILL_INPUT");
    break;
  case CV_NO_MALLOC:
    sprintf(name,"CV_NO_MALLOC");
    break;
  case CV_BAD_K:
    sprintf(name,"CV_BAD_K");
    break;
  case CV_BAD_T:
    sprintf(name,"CV_BAD_T");
    break;
  case CV_BAD_DKY:
    sprintf(name,"CV_BAD_DKY");
    break;
  case CV_NO_QUAD:
    sprintf(name,"CV_NO_QUAD");
    break;
  case CV_QRHSFUNC_FAIL:
    sprintf(name,"CV_QRHSFUNC_FAIL");
    break;
  case CV_FIRST_QRHSFUNC_ERR:
    sprintf(name,"CV_FIRST_QRHSFUNC_ERR");
    break;
  case CV_REPTD_QRHSFUNC_ERR:
    sprintf(name,"CV_REPTD_QRHSFUNC_ERR");
    break;
  case CV_UNREC_QRHSFUNC_ERR:
    sprintf(name,"CV_UNREC_QRHSFUNC_ERR");
    break;
  case CV_BAD_IS:
    sprintf(name,"CV_BAD_IS");
    break;
  case CV_NO_SENS:
    sprintf(name,"CV_NO_SENS");
    break;
  case CV_SRHSFUNC_FAIL:
    sprintf(name,"CV_SRHSFUNC_FAIL");
    break;
  case CV_FIRST_SRHSFUNC_ERR:
    sprintf(name,"CV_FIRST_SRHSFUNC_ERR");
    break;
  case CV_REPTD_SRHSFUNC_ERR:
    sprintf(name,"CV_REPTD_SRHSFUNC_ERR");
    break;
  case CV_UNREC_SRHSFUNC_ERR:
    sprintf(name,"CV_UNREC_SRHSFUNC_ERR");
    break;
  case CV_TOO_CLOSE:
    sprintf(name,"CV_TOO_CLOSE");
    break;
  case CV_NO_ADJ:
    sprintf(name,"CV_NO_ADJ");
    break;
  case CV_NO_FWD:
    sprintf(name,"CV_NO_FWD");
    break;
  case CV_NO_BCK:
    sprintf(name,"CV_NO_BCK");
    break;
  case CV_BAD_TB0:
    sprintf(name,"CV_BAD_TB0");
    break;
  case CV_REIFWD_FAIL:
    sprintf(name,"CV_REIFWD_FAIL");
    break;
  case CV_FWD_FAIL:
    sprintf(name,"CV_FWD_FAIL");
    break;
  case CV_GETY_BADT:
    sprintf(name,"CV_GETY_BADT");
    break;
  case CV_NLS_FAIL:
    sprintf(name,"CV_NLS_FAIL");
    break;
  default:
    sprintf(name,"NONE");
  }

  return(name);
}
