ESymSolverStatus IterativePardisoSolverInterface::Solve(const Index* ia,
      const Index* ja,
      Index nrhs,
      double *rhs_vals)
  {
    DBG_START_METH("IterativePardisoSolverInterface::Solve",dbg_verbosity);

    DBG_ASSERT(nrhs==1);

    if (HaveIpData()) {
      IpData().TimingStats().LinearSystemBackSolve().Start();
    }
    // Call Pardiso to do the solve for the given right-hand sides
    ipfint PHASE = 33;
    ipfint N = dim_;
    ipfint PERM;   // This should not be accessed by Pardiso
    ipfint NRHS = nrhs;
    double* X = new double[nrhs*dim_];
    double* ORIG_RHS = new double[nrhs*dim_];
    ipfint ERROR;

    // Initialize solution with zero and save right hand side
    for (int i = 0; i < N; i++) {
      X[i] = 0;
      ORIG_RHS[i] = rhs_vals[i];
    }

    // Dump matrix to file if requested
    Index iter_count = 0;
    if (HaveIpData()) {
      iter_count = IpData().iter_count();
    }
    write_iajaa_matrix (N, ia, ja, a_, rhs_vals, iter_count, debug_cnt_);

    IterativeSolverTerminationTester* tester;

    int attempts = 0;
    const int max_attempts = pardiso_max_droptol_corrections_+1;

    bool is_normal = false;
    if (IsNull(InexData().normal_x()) && InexData().compute_normal()) {
      tester = GetRawPtr(normal_tester_);
      is_normal = true;
    }
    else {
      tester = GetRawPtr(pd_tester_);
    }
    global_tester_ptr_ = tester;

    while (attempts<max_attempts) {
      bool retval = tester->InitializeSolve();
      ASSERT_EXCEPTION(retval, INTERNAL_ABORT, "tester->InitializeSolve(); returned false");

      for (int i = 0; i < N; i++) {
        rhs_vals[i] = ORIG_RHS[i];
      }

      DPARM_[ 8] = 25; // non_improvement in SQMR iteration
      F77_FUNC(pardiso,PARDISO)(PT_, &MAXFCT_, &MNUM_, &MTYPE_,
                                &PHASE, &N, a_, ia, ja, &PERM,
                                &NRHS, IPARM_, &MSGLVL_, rhs_vals, X,
                                &ERROR, DPARM_);

      if (ERROR <= -100 && ERROR >= -110) {
        Jnlst().Printf(J_WARNING, J_LINEAR_ALGEBRA,
                       "Iterative solver in Pardiso did not converge (ERROR = %d)\n", ERROR);
        Jnlst().Printf(J_WARNING, J_LINEAR_ALGEBRA,
                       "  Decreasing drop tolerances from DPARM_[ 4] = %e and DPARM_[ 5] = %e ", DPARM_[ 4], DPARM_[ 5]);
        if (is_normal) {
          Jnlst().Printf(J_WARNING, J_LINEAR_ALGEBRA,
                         "(normal step)\n");
        }
        else {
          Jnlst().Printf(J_WARNING, J_LINEAR_ALGEBRA,
                         "(PD step)\n");
        }
        PHASE = 23;
        DPARM_[ 4] *= decr_factor_;
        DPARM_[ 5] *= decr_factor_;
        Jnlst().Printf(J_WARNING, J_LINEAR_ALGEBRA,
                       "                               to DPARM_[ 4] = %e and DPARM_[ 5] = %e\n", DPARM_[ 4], DPARM_[ 5]);
        // Copy solution back to y to get intial values for the next iteration
        attempts++;
        ERROR = 0;
      }
      else  {
        attempts = max_attempts;
        Index iterations_used = tester->GetSolverIterations();
        if (is_normal) {
          Jnlst().Printf(J_DETAILED, J_LINEAR_ALGEBRA,
                         "Number of iterations in Pardiso iterative solver for normal step = %d.\n", iterations_used);
        }
        else {
          Jnlst().Printf(J_DETAILED, J_LINEAR_ALGEBRA,
                         "Number of iterations in Pardiso iterative solver for PD step = %d.\n", iterations_used);
        }
      }
      tester->Clear();
    }

    if (is_normal) {
      if (DPARM_[4] < normal_pardiso_iter_dropping_factor_) {
        Jnlst().Printf(J_DETAILED, J_LINEAR_ALGEBRA,
                       "Increasing drop tolerances from DPARM_[ 4] = %e and DPARM_[ 5] = %e (normal step\n", DPARM_[ 4], DPARM_[ 5]);
      }
      normal_pardiso_iter_dropping_factor_used_ =
        Min(DPARM_[4]/decr_factor_, normal_pardiso_iter_dropping_factor_);
      normal_pardiso_iter_dropping_schur_used_ =
        Min(DPARM_[5]/decr_factor_, normal_pardiso_iter_dropping_schur_);
      if (DPARM_[4] < normal_pardiso_iter_dropping_factor_) {
        Jnlst().Printf(J_DETAILED, J_LINEAR_ALGEBRA,
                       "                             to DPARM_[ 4] = %e and DPARM_[ 5] = %e for next iteration.\n", normal_pardiso_iter_dropping_factor_used_, normal_pardiso_iter_dropping_schur_used_);
      }
    }
    else {
      if (DPARM_[4] < pardiso_iter_dropping_factor_) {
        Jnlst().Printf(J_DETAILED, J_LINEAR_ALGEBRA,
                       "Increasing drop tolerances from DPARM_[ 4] = %e and DPARM_[ 5] = %e (PD step\n", DPARM_[ 4], DPARM_[ 5]);
      }
      pardiso_iter_dropping_factor_used_ =
        Min(DPARM_[4]/decr_factor_, pardiso_iter_dropping_factor_);
      pardiso_iter_dropping_schur_used_ =
        Min(DPARM_[5]/decr_factor_, pardiso_iter_dropping_schur_);
      if (DPARM_[4] < pardiso_iter_dropping_factor_) {
        Jnlst().Printf(J_DETAILED, J_LINEAR_ALGEBRA,
                       "                             to DPARM_[ 4] = %e and DPARM_[ 5] = %e for next iteration.\n", pardiso_iter_dropping_factor_used_, pardiso_iter_dropping_schur_used_);
      }
    }

    delete [] X;
    delete [] ORIG_RHS;

    if (IPARM_[6] != 0) {
      Jnlst().Printf(J_DETAILED, J_LINEAR_ALGEBRA,
                     "Number of iterative refinement steps = %d.\n", IPARM_[6]);
      if (HaveIpData()) {
        IpData().Append_info_string("Pi");
      }
    }

    if (HaveIpData()) {
      IpData().TimingStats().LinearSystemBackSolve().End();
    }
    if (ERROR!=0 ) {
      Jnlst().Printf(J_ERROR, J_LINEAR_ALGEBRA,
                     "Error in Pardiso during solve phase.  ERROR = %d.\n", ERROR);
      return SYMSOLVER_FATAL_ERROR;
    }
    if (test_result_ == IterativeSolverTerminationTester::MODIFY_HESSIAN) {
      Jnlst().Printf(J_DETAILED, J_LINEAR_ALGEBRA,
                     "Termination tester requests modification of Hessian\n");
      return SYMSOLVER_WRONG_INERTIA;
    }
#if 0
    // FRANK: look at this:
    if (test_result_ == IterativeSolverTerminationTester::CONTINUE) {
      if (InexData().compute_normal()) {
        Jnlst().Printf(J_DETAILED, J_LINEAR_ALGEBRA,
                       "Termination tester not satisfied!!! Pretend singular\n");
        return SYMSOLVER_SINGULAR;
      }
    }
#endif
    if (test_result_ == IterativeSolverTerminationTester::TEST_2_SATISFIED) {
      // Termination Test 2 is satisfied, set the step for the primal
      // iterates to zero
      Index nvars = IpData().curr()->x()->Dim() + IpData().curr()->s()->Dim();
      const Number zero = 0.;
      IpBlasDcopy(nvars, &zero, 0, rhs_vals, 1);
    }
    return SYMSOLVER_SUCCESS;
  }
  ESymSolverStatus PardisoSolverInterface::Solve(const Index* ia,
      const Index* ja,
      Index nrhs,
      double *rhs_vals)
  {
    DBG_START_METH("PardisoSolverInterface::Solve",dbg_verbosity);

    if (HaveIpData()) {
      IpData().TimingStats().LinearSystemBackSolve().Start();
    }
    // Call Pardiso to do the solve for the given right-hand sides
    ipfint PHASE = 33;
    ipfint N = dim_;
    ipfint PERM;   // This should not be accessed by Pardiso
    ipfint NRHS = nrhs;
    double* X = new double[nrhs*dim_];

    double* ORIG_RHS = new double[nrhs*dim_];
    ipfint ERROR;
    // Initialize solution with zero and save right hand side
    for (int i = 0; i < N; i++) {
      X[i] = 0.;
      ORIG_RHS[i] = rhs_vals[i];
    }

    // Dump matrix to file if requested
    Index iter_count = 0;
    if (HaveIpData()) {
      iter_count = IpData().iter_count();
    }

#ifdef PARDISO_MATCHING_PREPROCESS
    write_iajaa_matrix (N, ia2, ja2, a2_, rhs_vals, iter_count, debug_cnt_);
#else
    write_iajaa_matrix (N,  ia,  ja,  a_, rhs_vals, iter_count, debug_cnt_);
#endif

    int attempts = 0;
    const int max_attempts =
      pardiso_iterative_ ? pardiso_max_droptol_corrections_+1: 1;

    while (attempts < max_attempts) {


#ifdef PARDISO_MATCHING_PREPROCESS
      for (int i = 0; i < N; i++) {
        rhs_vals[perm2[i]] = scale2[i] * ORIG_RHS[ i  ];
      }
      PARDISO_FUNC(PT_, &MAXFCT_, &MNUM_, &MTYPE_,
                   &PHASE, &N, a2_, ia2, ja2, &PERM,
                   &NRHS, IPARM_, &MSGLVL_, rhs_vals, X,
                   &ERROR, DPARM_);
      for (int i = 0; i < N; i++) {
        X[i] = rhs_vals[ perm2[i]];
      }
      for (int i = 0; i < N; i++) {
        rhs_vals[i] =  scale2[i]*X[i];
      }

#else
      for (int i = 0; i < N; i++) {
        rhs_vals[i] = ORIG_RHS[i];
      }
      PARDISO_FUNC(PT_, &MAXFCT_, &MNUM_, &MTYPE_,
                   &PHASE, &N, a_, ia, ja, &PERM,
                   &NRHS, IPARM_, &MSGLVL_, rhs_vals, X,
                   &ERROR, DPARM_);
#endif


      if (ERROR <= -100 && ERROR >= -102) {
        Jnlst().Printf(J_WARNING, J_LINEAR_ALGEBRA,
                       "Iterative solver in Pardiso did not converge (ERROR = %d)\n", ERROR);
        Jnlst().Printf(J_WARNING, J_LINEAR_ALGEBRA,
                       "  Decreasing drop tolerances from DPARM_[41] = %e and DPARM_[44] = %e\n", DPARM_[41], DPARM_[44]);
        PHASE = 23;
        DPARM_[4] /= 2.0 ;
        DPARM_[5] /= 2.0 ;
        Jnlst().Printf(J_WARNING, J_LINEAR_ALGEBRA,
                       "                               to DPARM_[41] = %e and DPARM_[44] = %e\n", DPARM_[41], DPARM_[44]);
        attempts++;
        ERROR = 0;
      }
      else {
        attempts = max_attempts;
        // TODO we could try again with some PARDISO parameters changed, i.e., enabling iterative refinement
      }
    }

    delete [] X;
    delete [] ORIG_RHS;

    if (IPARM_[6] != 0) {
      Jnlst().Printf(J_DETAILED, J_LINEAR_ALGEBRA,
                     "Number of iterative refinement steps = %d.\n", IPARM_[6]);
      if (HaveIpData()) {
        IpData().Append_info_string("Pi");
      }
    }

    if (HaveIpData()) {
      IpData().TimingStats().LinearSystemBackSolve().End();
    }
    if (ERROR!=0 ) {
      Jnlst().Printf(J_ERROR, J_LINEAR_ALGEBRA,
                     "Error in Pardiso during solve phase.  ERROR = %d.\n", ERROR);
      return SYMSOLVER_FATAL_ERROR;
    }
    return SYMSOLVER_SUCCESS;
  }