void jex(const double tn,
         ADBL  *x,
         Eigen::Matrix<ADBL, Eigen::Dynamic, Eigen::Dynamic>& wm) {
}
void jex(const double tn,
         AVAR  *x,
         Eigen::Matrix<AVAR, Eigen::Dynamic, Eigen::Dynamic>& wm) {
}

template <class T, class T0>
Eigen::Matrix<T,Eigen::Dynamic,1>
generic_ode_interface_multi_state(Eigen::Matrix<T,Eigen::Dynamic,1>& params,
                      const Eigen::VectorXd& inits,
                      const Eigen::VectorXd& time,
                      const Eigen::VectorXd& evid,
                      const Eigen::VectorXd& amt,
                      const T0& absolute_tolerance,
                      const T0& relative_tolerance,
                      const Eigen::VectorXd& offset,
                      const int nobs) {
  const char *err_msg[] = {
    "excess work done on this call (perhaps wrong jt).",
    "excess accuracy requested (tolerances too small).",
    "illegal input detected (see printed message).",
    "repeated error test failures (check all inputs).",
    "repeated convergence failures (perhaps bad jacobian supplied or wrong choice of jt or tolerances).",
    "error weight became zero during problem. (solution component i vanished, and atol or atol(i) = 0.)",
    "work space insufficient to finish (see messages)."
  };

#ifdef __TRACK_ELAPSE__
  std::clock_t start;
  double duration;

  start = std::clock();
#endif
    
    
  for (int i = 0; i < params.size(); i++) {
	if (std::isinf(params[i]) || params[i] > 1.0E200 || params[i] < -1.0E200) {
	  std::cerr 
	    << "WARNING!!!\n"
	    << "extreme parameter value:  "
	    << "parameter " << i+1
	    << ", value = " << params[i] 
	    << std::endl;
	}
  }
  
    
  typedef Eigen::Matrix<T, Eigen::Dynamic, 1> VectorXd;
  VectorXd state(neq+1);
  LSODA<T> ode_solver;

  for (size_t i = 0; i < neq; i++)
  {
    state[i+1] = inits[i];
  }
  for (size_t i = 0; i < npar; i++)
  {
    pars_var[i] = params[i];
    pars_dbl[i] = pars_var[i].val();
  }

  std::vector<int> o(neq);  //offset (double) to o (int)
  for (int i = 0; i < offset.size(); i++) {
	o[i] = offset[i];
  }
  std::vector<int> ix(neq,0);	//accumulative obs ix of different state vars.
  							//FIXME: make sure we init it @ 0
  int which;	//current state var index
  VectorXd C(nobs);
  double t0, t1;
  double rwork1, rwork5, rwork6, rwork7;
  double atol=absolute_tolerance, rtol=relative_tolerance;
  int    iwork1, iwork2, iwork5, iwork6, iwork7, iwork8, iwork9;
  int    itol=1, itask=1, istate=1, iopt=0, jt=2;
  iwork1 = iwork2 = iwork5 = iwork6 = iwork7 = iwork8 = iwork9 = 0;
  rwork1 = rwork5 = rwork6 = rwork7 = 0.0;
  int wh, cmt;

  t0 = time[0];
  for (int i = 0, n = 0; i < time.size(); i++) {
    t1 = time[i];

    if(t1>t0) {
      ode_solver.lsoda(dydt, neq, state, &t0, t1, itol, &rtol-1, &atol-1,
             itask, &istate, iopt, jt, jex, iwork1, iwork2, iwork5, iwork6,
             iwork7, iwork8, iwork9, rwork1, rwork5, rwork6, rwork7, 0);
      if (istate<0) {
        std::cerr << "LSODA exception: " << err_msg[-istate-1] << std::endl;
        std::cerr << params << std::endl;
        std::cerr << time << std::endl;
        std::cerr << amt << std::endl;
        std::cerr << evid << std::endl;
	  }
    }

    wh = evid[i];

    if (wh>0) {    //dosing events
      cmt = (wh % 10000) / 100 - 1;
      if (wh > 10000)
        InfusionRate[cmt] += amt[i];
      else
        state[cmt+1] += amt[i];    //dosing before obs
      istate = 1;
    }
    else {      //observation events
      which = -wh - 1;
      C[o[which]+ix[which]] = state[which+1];	//note lsoda is from fortran, the 1st state var start at 1
      ix[which] += 1;
    }

    t0 = t1;
  }


#ifdef __TRACK_ELAPSE__
  duration = ( std::clock() - start ) / (double) CLOCKS_PER_SEC;
  if (duration>__TRACK_ELAPSE__) {
    std::cout<<"lsoda duration: " << duration <<'\n';
    std::cout<<"params:\n" << params <<'\n';
  }
#endif

  //ode_solver.n_lsoda_terminate();
  return C;
}
