#include <test/unit/math/torsten/pmx_onecpt_test_fixture.hpp>
#include <test/unit/math/torsten/pmx_twocpt_test_fixture.hpp>
#include <test/unit/math/torsten/pmx_twocpt_mpi_test_fixture.hpp>
#include <test/unit/math/torsten/test_util.hpp>
#include <Eigen/Dense>
#include <Eigen/src/Core/NumTraits.h>
#include <gtest/gtest.h>
#include <vector>

using std::vector;
using Eigen::Matrix;
using Eigen::Dynamic;
using stan::math::var;
using refactor::PKRec;
using torsten::EventsManager;
using torsten::NONMENEventsRecord;

TEST_F(TorstenOneCptTest, lag_time) {
  using stan::math::var;

  nt = 2;
  time.resize(nt);
  amt.resize(nt);
  rate.resize(nt);
  cmt.resize(nt);
  evid.resize(nt);
  ii.resize(nt);
  addl.resize(nt);
  ss.resize(nt);
  evid[0] = 1;
  cmt[0] = 1;
  ii[0] = 0;
  addl[0] = 0;
  time[0] = 0.0;
  tlag[0][0] = 1.5;

  time[1] = 2.5;

  const NONMENEventsRecord<double, double, double, double, std::vector<double>, double, double>
    events_rec(nCmt, time, amt, rate, ii, evid, cmt, addl, ss, pMatrix, biovar, tlag);
  EventsManager<NONMENEventsRecord<double, double, double, double, std::vector<double>, double, double> >
    em(events_rec);
  auto ev = em.events();

  EXPECT_EQ(ev.num_state_times(), nt + 1);

  EXPECT_FLOAT_EQ(ev.time(0), 0.0);
  EXPECT_FLOAT_EQ(ev.time(1), 1.5);
  EXPECT_FLOAT_EQ(ev.time(2), 2.5);
  EXPECT_EQ(ev.evid(0), 2);
  EXPECT_EQ(ev.evid(1), 1);
  EXPECT_EQ(ev.evid(2), 0);

  std::vector<std::vector<var> > tlag_v(1, stan::math::to_var(tlag[0]));
  const NONMENEventsRecord<double, double, double, double, std::vector<double>, double, var>
    events_rec_v(nCmt, time, amt, rate, ii, evid, cmt, addl, ss, pMatrix, biovar, tlag_v);
  EventsManager<NONMENEventsRecord<double, double, double, double, std::vector<double>, double, var> >
    em1(events_rec_v);
  auto ev1 = em1.events();
  stan::math::set_zero_all_adjoints();
  std::vector<double> g;
  ev1.time(1).grad(tlag_v[0], g);
  EXPECT_FLOAT_EQ(g[0], 1.0);
  EXPECT_FLOAT_EQ(g[1], 0.0);
}

TEST_F(TorstenTwoCptTest, events_addl) {
  using ER = NONMENEventsRecord<double, double, double, double, std::vector<double>, double, double>;
  using EM = EventsManager<ER>;

  int nCmt = refactor::PMXTwoCptModel<double, double, double, double>::Ncmt;
  addl[0] = 5;
  addl[3] = 3;

  {
    ER events_rec(nCmt, time, amt, rate, ii, evid, cmt, addl, ss, pMatrix, biovar, tlag);
    EM em(events_rec);
    auto ev = em.events();
    EXPECT_EQ(ev.num_state_times(), evid.size() + addl[0]);
    EXPECT_EQ(ev.num_state_times(), EM::num_events(events_rec) );
  }

  {
    ii[3] = 4.0;
    ER events_rec(nCmt, time, amt, rate, ii, evid, cmt, addl, ss, pMatrix, biovar, tlag);
    EM em(events_rec);
    auto ev = em.events();
    EXPECT_EQ(ev.num_state_times(), evid.size() + addl[0]);
    EXPECT_EQ(ev.num_state_times(), EM::num_events(events_rec) );
  }

  amt[3] = 400.0;
  evid[3] = 1;
  ER events_rec(nCmt, time, amt, rate, ii, evid, cmt, addl, ss, pMatrix, biovar, tlag);
  EM em(events_rec);
  auto ev = em.events();
  EXPECT_EQ(ev.num_state_times(), evid.size() + addl[0] + addl[3]);
  EXPECT_EQ(ev.num_state_times(), EM::num_events(events_rec) );

  EXPECT_EQ(ev.time(0 ), 0    );  EXPECT_EQ(ev.amt(0 ), 1000);  EXPECT_EQ(ev.evid(0 ), 1);
  EXPECT_EQ(ev.time(1 ), 0.25 );  EXPECT_EQ(ev.amt(1 ), 0   );  EXPECT_EQ(ev.evid(1 ), 0);
  EXPECT_EQ(ev.time(2 ), 0.5  );  EXPECT_EQ(ev.amt(2 ), 0   );  EXPECT_EQ(ev.evid(2 ), 0);
  EXPECT_EQ(ev.time(3 ), 0.75 );  EXPECT_EQ(ev.amt(3 ), 400 );  EXPECT_EQ(ev.evid(3 ), 1);
  EXPECT_EQ(ev.time(4 ), 1    );  EXPECT_EQ(ev.amt(4 ), 0   );  EXPECT_EQ(ev.evid(4 ), 0);
  EXPECT_EQ(ev.time(5 ), 1.25 );  EXPECT_EQ(ev.amt(5 ), 0   );  EXPECT_EQ(ev.evid(5 ), 0);
  EXPECT_EQ(ev.time(6 ), 1.5  );  EXPECT_EQ(ev.amt(6 ), 0   );  EXPECT_EQ(ev.evid(6 ), 0);
  EXPECT_EQ(ev.time(7 ), 1.75 );  EXPECT_EQ(ev.amt(7 ), 0   );  EXPECT_EQ(ev.evid(7 ), 0);
  EXPECT_EQ(ev.time(8 ), 2    );  EXPECT_EQ(ev.amt(8 ), 0   );  EXPECT_EQ(ev.evid(8 ), 0);
  EXPECT_EQ(ev.time(9 ), 4    );  EXPECT_EQ(ev.amt(9 ), 0   );  EXPECT_EQ(ev.evid(9 ), 0);
  EXPECT_EQ(ev.time(10), 4.75 );  EXPECT_EQ(ev.amt(10), 400 );  EXPECT_EQ(ev.evid(10), 1);
  EXPECT_EQ(ev.time(11), 8.75 );  EXPECT_EQ(ev.amt(11), 400 );  EXPECT_EQ(ev.evid(11), 1);
  EXPECT_EQ(ev.time(12), 12   );  EXPECT_EQ(ev.amt(12), 1000);  EXPECT_EQ(ev.evid(12), 1);
  EXPECT_EQ(ev.time(13), 12.75);  EXPECT_EQ(ev.amt(13), 400 );  EXPECT_EQ(ev.evid(13), 1);
  EXPECT_EQ(ev.time(14), 24   );  EXPECT_EQ(ev.amt(14), 1000);  EXPECT_EQ(ev.evid(14), 1);
  EXPECT_EQ(ev.time(15), 36   );  EXPECT_EQ(ev.amt(15), 1000);  EXPECT_EQ(ev.evid(15), 1);
  EXPECT_EQ(ev.time(16), 48   );  EXPECT_EQ(ev.amt(16), 1000);  EXPECT_EQ(ev.evid(16), 1);
  EXPECT_EQ(ev.time(17), 60   );  EXPECT_EQ(ev.amt(17), 1000);  EXPECT_EQ(ev.evid(17), 1);
}

TEST_F(TorstenTwoCptTest, events_addl_singled_ragged_array) {
  using ER = NONMENEventsRecord<double, double, double, double, std::vector<double>, double, double>;
  using EM = EventsManager<ER>;

  int nCmt = refactor::PMXTwoCptModel<double, double, double, double>::Ncmt;
  addl[0] = 5;
  addl[3] = 3;

  {
    ER events_rec(nCmt, time, amt, rate, ii, evid, cmt, addl, ss, pMatrix, biovar, tlag);
    EM em(0, events_rec);
    auto ev = em.events();
    EXPECT_EQ(ev.num_state_times(), evid.size() + addl[0]);
    EXPECT_EQ(ev.num_state_times(), EM::num_events(events_rec) );
  }

  {
    ii[3] = 4.0;
    ER events_rec(nCmt, time, amt, rate, ii, evid, cmt, addl, ss, pMatrix, biovar, tlag);
    EM em(0, events_rec);
    auto ev = em.events();
    EXPECT_EQ(ev.num_state_times(), evid.size() + addl[0]);
    EXPECT_EQ(ev.num_state_times(), EM::num_events(events_rec) );
  }

  amt[3] = 400.0;
  evid[3] = 1;
  ER events_rec(nCmt, time, amt, rate, ii, evid, cmt, addl, ss, pMatrix, biovar, tlag);
  EM em(0, events_rec);
  auto ev = em.events();
  EXPECT_EQ(ev.num_state_times(), evid.size() + addl[0] + addl[3]);
  EXPECT_EQ(ev.num_state_times(), EM::num_events(events_rec) );

  EXPECT_EQ(ev.time(0 ), 0    );  EXPECT_EQ(ev.amt(0 ), 1000);  EXPECT_EQ(ev.evid(0 ), 1);
  EXPECT_EQ(ev.time(1 ), 0.25 );  EXPECT_EQ(ev.amt(1 ), 0   );  EXPECT_EQ(ev.evid(1 ), 0);
  EXPECT_EQ(ev.time(2 ), 0.5  );  EXPECT_EQ(ev.amt(2 ), 0   );  EXPECT_EQ(ev.evid(2 ), 0);
  EXPECT_EQ(ev.time(3 ), 0.75 );  EXPECT_EQ(ev.amt(3 ), 400 );  EXPECT_EQ(ev.evid(3 ), 1);
  EXPECT_EQ(ev.time(4 ), 1    );  EXPECT_EQ(ev.amt(4 ), 0   );  EXPECT_EQ(ev.evid(4 ), 0);
  EXPECT_EQ(ev.time(5 ), 1.25 );  EXPECT_EQ(ev.amt(5 ), 0   );  EXPECT_EQ(ev.evid(5 ), 0);
  EXPECT_EQ(ev.time(6 ), 1.5  );  EXPECT_EQ(ev.amt(6 ), 0   );  EXPECT_EQ(ev.evid(6 ), 0);
  EXPECT_EQ(ev.time(7 ), 1.75 );  EXPECT_EQ(ev.amt(7 ), 0   );  EXPECT_EQ(ev.evid(7 ), 0);
  EXPECT_EQ(ev.time(8 ), 2    );  EXPECT_EQ(ev.amt(8 ), 0   );  EXPECT_EQ(ev.evid(8 ), 0);
  EXPECT_EQ(ev.time(9 ), 4    );  EXPECT_EQ(ev.amt(9 ), 0   );  EXPECT_EQ(ev.evid(9 ), 0);
  EXPECT_EQ(ev.time(10), 4.75 );  EXPECT_EQ(ev.amt(10), 400 );  EXPECT_EQ(ev.evid(10), 1);
  EXPECT_EQ(ev.time(11), 8.75 );  EXPECT_EQ(ev.amt(11), 400 );  EXPECT_EQ(ev.evid(11), 1);
  EXPECT_EQ(ev.time(12), 12   );  EXPECT_EQ(ev.amt(12), 1000);  EXPECT_EQ(ev.evid(12), 1);
  EXPECT_EQ(ev.time(13), 12.75);  EXPECT_EQ(ev.amt(13), 400 );  EXPECT_EQ(ev.evid(13), 1);
  EXPECT_EQ(ev.time(14), 24   );  EXPECT_EQ(ev.amt(14), 1000);  EXPECT_EQ(ev.evid(14), 1);
  EXPECT_EQ(ev.time(15), 36   );  EXPECT_EQ(ev.amt(15), 1000);  EXPECT_EQ(ev.evid(15), 1);
  EXPECT_EQ(ev.time(16), 48   );  EXPECT_EQ(ev.amt(16), 1000);  EXPECT_EQ(ev.evid(16), 1);
  EXPECT_EQ(ev.time(17), 60   );  EXPECT_EQ(ev.amt(17), 1000);  EXPECT_EQ(ev.evid(17), 1);
}

TEST_F(TorstenTwoCptTest, events_addl_multiple_identical_ragged_array) {
  using ER = NONMENEventsRecord<double, double, double, double, std::vector<double>, double, double>;
  using EM = EventsManager<ER>;

  int nCmt = refactor::PMXTwoCptModel<double, double, double, double>::Ncmt;
  addl[0] = 5;
  addl[3] = 3;
  ii[3] = 4.0;
  amt[3] = 400.0;
  evid[3] = 1;

  size_t n = time.size();
  time.resize (2 * n) ; std::copy_n(time.begin() , n, time.begin() + n);
  amt.resize  (2 * n) ; std::copy_n(amt.begin()  , n, amt.begin()  + n);
  rate.resize (2 * n) ; std::copy_n(rate.begin() , n, rate.begin() + n);
  ii.resize   (2 * n) ; std::copy_n(ii.begin()   , n, ii.begin()   + n);
  evid.resize (2 * n) ; std::copy_n(evid.begin() , n, evid.begin() + n);
  cmt.resize  (2 * n) ; std::copy_n(cmt.begin()  , n, cmt.begin()  + n);
  addl.resize (2 * n) ; std::copy_n(addl.begin() , n, addl.begin() + n);
  ss.resize   (2 * n) ; std::copy_n(ss.begin()   , n, ss.begin()   + n);

  size_t n1 = pMatrix.size();
  size_t n2 = biovar.size();
  size_t n3 = tlag.size();
  pMatrix.resize(2 * n1) ; std::copy_n(pMatrix.begin(), n1, pMatrix.begin() + n1);
  biovar.resize (2 * n2) ; std::copy_n(biovar.begin(),  n2, biovar.begin()  + n2);
  tlag.resize   (2 * n3) ; std::copy_n(tlag.begin(),    n3, tlag.begin()    + n3);

  std::vector<int> len(2, n);
  ER events_rec(nCmt, len, time, amt, rate, ii, evid, cmt, addl, ss, pMatrix, biovar, tlag);
  {
    EM em(0, events_rec, 0, n1, 0, n2, 0, n3);
    auto ev = em.events();
    EXPECT_EQ(ev.num_state_times(), EM::num_events(0, events_rec) );
    EXPECT_EQ(ev.time(0 ), 0    );  EXPECT_EQ(ev.amt(0 ), 1000);  EXPECT_EQ(ev.evid(0 ), 1);
    EXPECT_EQ(ev.time(1 ), 0.25 );  EXPECT_EQ(ev.amt(1 ), 0   );  EXPECT_EQ(ev.evid(1 ), 0);
    EXPECT_EQ(ev.time(2 ), 0.5  );  EXPECT_EQ(ev.amt(2 ), 0   );  EXPECT_EQ(ev.evid(2 ), 0);
    EXPECT_EQ(ev.time(3 ), 0.75 );  EXPECT_EQ(ev.amt(3 ), 400 );  EXPECT_EQ(ev.evid(3 ), 1);
    EXPECT_EQ(ev.time(4 ), 1    );  EXPECT_EQ(ev.amt(4 ), 0   );  EXPECT_EQ(ev.evid(4 ), 0);
    EXPECT_EQ(ev.time(5 ), 1.25 );  EXPECT_EQ(ev.amt(5 ), 0   );  EXPECT_EQ(ev.evid(5 ), 0);
    EXPECT_EQ(ev.time(6 ), 1.5  );  EXPECT_EQ(ev.amt(6 ), 0   );  EXPECT_EQ(ev.evid(6 ), 0);
    EXPECT_EQ(ev.time(7 ), 1.75 );  EXPECT_EQ(ev.amt(7 ), 0   );  EXPECT_EQ(ev.evid(7 ), 0);
    EXPECT_EQ(ev.time(8 ), 2    );  EXPECT_EQ(ev.amt(8 ), 0   );  EXPECT_EQ(ev.evid(8 ), 0);
    EXPECT_EQ(ev.time(9 ), 4    );  EXPECT_EQ(ev.amt(9 ), 0   );  EXPECT_EQ(ev.evid(9 ), 0);
    EXPECT_EQ(ev.time(10), 4.75 );  EXPECT_EQ(ev.amt(10), 400 );  EXPECT_EQ(ev.evid(10), 1);
    EXPECT_EQ(ev.time(11), 8.75 );  EXPECT_EQ(ev.amt(11), 400 );  EXPECT_EQ(ev.evid(11), 1);
    EXPECT_EQ(ev.time(12), 12   );  EXPECT_EQ(ev.amt(12), 1000);  EXPECT_EQ(ev.evid(12), 1);
    EXPECT_EQ(ev.time(13), 12.75);  EXPECT_EQ(ev.amt(13), 400 );  EXPECT_EQ(ev.evid(13), 1);
    EXPECT_EQ(ev.time(14), 24   );  EXPECT_EQ(ev.amt(14), 1000);  EXPECT_EQ(ev.evid(14), 1);
    EXPECT_EQ(ev.time(15), 36   );  EXPECT_EQ(ev.amt(15), 1000);  EXPECT_EQ(ev.evid(15), 1);
    EXPECT_EQ(ev.time(16), 48   );  EXPECT_EQ(ev.amt(16), 1000);  EXPECT_EQ(ev.evid(16), 1);
    EXPECT_EQ(ev.time(17), 60   );  EXPECT_EQ(ev.amt(17), 1000);  EXPECT_EQ(ev.evid(17), 1);
  }

  {
    EM em(1, events_rec, n1, n1, n2, n2, n3, n3);
    auto ev = em.events();
    EXPECT_EQ(ev.num_state_times(), EM::num_events(1, events_rec));
    EXPECT_EQ(ev.time(0 ), 0    );  EXPECT_EQ(ev.amt(0 ), 1000);  EXPECT_EQ(ev.evid(0 ), 1);
    EXPECT_EQ(ev.time(1 ), 0.25 );  EXPECT_EQ(ev.amt(1 ), 0   );  EXPECT_EQ(ev.evid(1 ), 0);
    EXPECT_EQ(ev.time(2 ), 0.5  );  EXPECT_EQ(ev.amt(2 ), 0   );  EXPECT_EQ(ev.evid(2 ), 0);
    EXPECT_EQ(ev.time(3 ), 0.75 );  EXPECT_EQ(ev.amt(3 ), 400 );  EXPECT_EQ(ev.evid(3 ), 1);
    EXPECT_EQ(ev.time(4 ), 1    );  EXPECT_EQ(ev.amt(4 ), 0   );  EXPECT_EQ(ev.evid(4 ), 0);
    EXPECT_EQ(ev.time(5 ), 1.25 );  EXPECT_EQ(ev.amt(5 ), 0   );  EXPECT_EQ(ev.evid(5 ), 0);
    EXPECT_EQ(ev.time(6 ), 1.5  );  EXPECT_EQ(ev.amt(6 ), 0   );  EXPECT_EQ(ev.evid(6 ), 0);
    EXPECT_EQ(ev.time(7 ), 1.75 );  EXPECT_EQ(ev.amt(7 ), 0   );  EXPECT_EQ(ev.evid(7 ), 0);
    EXPECT_EQ(ev.time(8 ), 2    );  EXPECT_EQ(ev.amt(8 ), 0   );  EXPECT_EQ(ev.evid(8 ), 0);
    EXPECT_EQ(ev.time(9 ), 4    );  EXPECT_EQ(ev.amt(9 ), 0   );  EXPECT_EQ(ev.evid(9 ), 0);
    EXPECT_EQ(ev.time(10), 4.75 );  EXPECT_EQ(ev.amt(10), 400 );  EXPECT_EQ(ev.evid(10), 1);
    EXPECT_EQ(ev.time(11), 8.75 );  EXPECT_EQ(ev.amt(11), 400 );  EXPECT_EQ(ev.evid(11), 1);
    EXPECT_EQ(ev.time(12), 12   );  EXPECT_EQ(ev.amt(12), 1000);  EXPECT_EQ(ev.evid(12), 1);
    EXPECT_EQ(ev.time(13), 12.75);  EXPECT_EQ(ev.amt(13), 400 );  EXPECT_EQ(ev.evid(13), 1);
    EXPECT_EQ(ev.time(14), 24   );  EXPECT_EQ(ev.amt(14), 1000);  EXPECT_EQ(ev.evid(14), 1);
    EXPECT_EQ(ev.time(15), 36   );  EXPECT_EQ(ev.amt(15), 1000);  EXPECT_EQ(ev.evid(15), 1);
    EXPECT_EQ(ev.time(16), 48   );  EXPECT_EQ(ev.amt(16), 1000);  EXPECT_EQ(ev.evid(16), 1);
    EXPECT_EQ(ev.time(17), 60   );  EXPECT_EQ(ev.amt(17), 1000);  EXPECT_EQ(ev.evid(17), 1);
  }
}

TEST_F(TorstenTwoCptTest, events_addl_rate) {
  using ER = NONMENEventsRecord<double, double, double, double, std::vector<double>, double, double>;
  using EM = EventsManager<ER>;

  int nCmt = refactor::PMXTwoCptModel<double, double, double, double>::Ncmt;
  addl[0] = 5;
  addl[3] = 2;
  amt[3] = 1200.0;
  ii[3] = 5;
  cmt[3] = 2;
  rate[3] = 400;
  evid[3] = 1;

  ER events_rec(nCmt, time, amt, rate, ii, evid, cmt, addl, ss, pMatrix, biovar, tlag);
  EM em(events_rec);
  auto ev = em.events();

  /* each IV dose has an end event.*/
  EXPECT_EQ(ev.num_state_times(), evid.size() + addl[0] + addl[3] * 2 + 1);
  EXPECT_EQ(ev.num_state_times(), EM::num_events(events_rec) );

  EXPECT_FLOAT_EQ(ev.time(0 ), 0    );   EXPECT_EQ(ev.evid(0 ), 1);
  EXPECT_FLOAT_EQ(ev.time(1 ), 0.25 );   EXPECT_EQ(ev.evid(1 ), 0);
  EXPECT_FLOAT_EQ(ev.time(2 ), 0.5  );   EXPECT_EQ(ev.evid(2 ), 0);
  EXPECT_FLOAT_EQ(ev.time(3 ), 0.75 );   EXPECT_EQ(ev.evid(3 ), 1);
  EXPECT_FLOAT_EQ(ev.time(4 ), 1    );   EXPECT_EQ(ev.evid(4 ), 0);
  EXPECT_FLOAT_EQ(ev.time(5 ), 1.25 );   EXPECT_EQ(ev.evid(5 ), 0);
  EXPECT_FLOAT_EQ(ev.time(6 ), 1.5  );   EXPECT_EQ(ev.evid(6 ), 0);
  EXPECT_FLOAT_EQ(ev.time(7 ), 1.75 );   EXPECT_EQ(ev.evid(7 ), 0);
  EXPECT_FLOAT_EQ(ev.time(8 ), 2    );   EXPECT_EQ(ev.evid(8 ), 0);
  EXPECT_FLOAT_EQ(ev.time(9 ), 3.75 );   EXPECT_EQ(ev.evid(9 ), 2);
  EXPECT_FLOAT_EQ(ev.time(10), 4    );   EXPECT_EQ(ev.evid(10), 0);
  EXPECT_FLOAT_EQ(ev.time(11), 5.75 );   EXPECT_EQ(ev.evid(11), 1);
  EXPECT_FLOAT_EQ(ev.time(12), 8.75 );   EXPECT_EQ(ev.evid(12), 2);
  EXPECT_FLOAT_EQ(ev.time(13), 10.75);   EXPECT_EQ(ev.evid(13), 1);
  EXPECT_FLOAT_EQ(ev.time(14), 12   );   EXPECT_EQ(ev.evid(14), 1);
  EXPECT_FLOAT_EQ(ev.time(15), 13.75);   EXPECT_EQ(ev.evid(15), 2);
  EXPECT_FLOAT_EQ(ev.time(16), 24   );   EXPECT_EQ(ev.evid(16), 1);
  EXPECT_FLOAT_EQ(ev.time(17), 36   );   EXPECT_EQ(ev.evid(17), 1);
  EXPECT_FLOAT_EQ(ev.time(18), 48   );   EXPECT_EQ(ev.evid(18), 1);
  EXPECT_FLOAT_EQ(ev.time(19), 60   );   EXPECT_EQ(ev.evid(19), 1);

  EXPECT_FLOAT_EQ(ev.fractioned_rates(0 )[1], 0  );
  EXPECT_FLOAT_EQ(ev.fractioned_rates(1 )[1], 0  );
  EXPECT_FLOAT_EQ(ev.fractioned_rates(2 )[1], 0  );
  EXPECT_FLOAT_EQ(ev.fractioned_rates(3 )[1], 0  );
  EXPECT_FLOAT_EQ(ev.fractioned_rates(4 )[1], 400);
  EXPECT_FLOAT_EQ(ev.fractioned_rates(5 )[1], 400);
  EXPECT_FLOAT_EQ(ev.fractioned_rates(6 )[1], 400);
  EXPECT_FLOAT_EQ(ev.fractioned_rates(7 )[1], 400);
  EXPECT_FLOAT_EQ(ev.fractioned_rates(8 )[1], 400);
  EXPECT_FLOAT_EQ(ev.fractioned_rates(9 )[1], 400);
  EXPECT_FLOAT_EQ(ev.fractioned_rates(10)[1], 0  );
  EXPECT_FLOAT_EQ(ev.fractioned_rates(11)[1], 0  );
  EXPECT_FLOAT_EQ(ev.fractioned_rates(12)[1], 400);
  EXPECT_FLOAT_EQ(ev.fractioned_rates(13)[1], 0  );
  EXPECT_FLOAT_EQ(ev.fractioned_rates(14)[1], 400);
  EXPECT_FLOAT_EQ(ev.fractioned_rates(15)[1], 400);
  EXPECT_FLOAT_EQ(ev.fractioned_rates(16)[1], 0  );
  EXPECT_FLOAT_EQ(ev.fractioned_rates(17)[1], 0  );
  EXPECT_FLOAT_EQ(ev.fractioned_rates(18)[1], 0  );
  EXPECT_FLOAT_EQ(ev.fractioned_rates(19)[1], 0  );
}

TEST_F(TorstenTwoCptTest, events_addl_rate_multiple_identical_ragged_array) {
  using ER = NONMENEventsRecord<double, double, double, double, std::vector<double>, double, double>;
  using EM = EventsManager<ER>;

  int nCmt = refactor::PMXTwoCptModel<double, double, double, double>::Ncmt;
  addl[0] = 5;
  addl[3] = 2;
  amt[3] = 1200.0;
  ii[3] = 5;
  cmt[3] = 2;
  rate[3] = 400;
  evid[3] = 1;

  size_t n = time.size();
  time.resize (2 * n) ; std::copy_n(time.begin() , n, time.begin() + n);
  amt.resize  (2 * n) ; std::copy_n(amt.begin()  , n, amt.begin()  + n);
  rate.resize (2 * n) ; std::copy_n(rate.begin() , n, rate.begin() + n);
  ii.resize   (2 * n) ; std::copy_n(ii.begin()   , n, ii.begin()   + n);
  evid.resize (2 * n) ; std::copy_n(evid.begin() , n, evid.begin() + n);
  cmt.resize  (2 * n) ; std::copy_n(cmt.begin()  , n, cmt.begin()  + n);
  addl.resize (2 * n) ; std::copy_n(addl.begin() , n, addl.begin() + n);
  ss.resize   (2 * n) ; std::copy_n(ss.begin()   , n, ss.begin()   + n);

  size_t n1 = pMatrix.size();
  size_t n2 = biovar.size();
  size_t n3 = tlag.size();
  pMatrix.resize(2 * n1) ; std::copy_n(pMatrix.begin(), n1, pMatrix.begin() + n1);
  biovar.resize (2 * n2) ; std::copy_n(biovar.begin(),  n2, biovar.begin()  + n2);
  tlag.resize   (2 * n3) ; std::copy_n(tlag.begin(),    n3, tlag.begin()    + n3);

  std::vector<int> len(2, n);
  ER events_rec(nCmt, len, time, amt, rate, ii, evid, cmt, addl, ss, pMatrix, biovar, tlag);
  {
    EM em(0, events_rec, 0, n1, 0, n2, 0, n3);
    auto ev = em.events();
    EXPECT_EQ(ev.num_state_times(), EM::num_events(0, events_rec) );
    EXPECT_FLOAT_EQ(ev.time(0 ), 0    );   EXPECT_EQ(ev.evid(0 ), 1);
    EXPECT_FLOAT_EQ(ev.time(1 ), 0.25 );   EXPECT_EQ(ev.evid(1 ), 0);
    EXPECT_FLOAT_EQ(ev.time(2 ), 0.5  );   EXPECT_EQ(ev.evid(2 ), 0);
    EXPECT_FLOAT_EQ(ev.time(3 ), 0.75 );   EXPECT_EQ(ev.evid(3 ), 1);
    EXPECT_FLOAT_EQ(ev.time(4 ), 1    );   EXPECT_EQ(ev.evid(4 ), 0);
    EXPECT_FLOAT_EQ(ev.time(5 ), 1.25 );   EXPECT_EQ(ev.evid(5 ), 0);
    EXPECT_FLOAT_EQ(ev.time(6 ), 1.5  );   EXPECT_EQ(ev.evid(6 ), 0);
    EXPECT_FLOAT_EQ(ev.time(7 ), 1.75 );   EXPECT_EQ(ev.evid(7 ), 0);
    EXPECT_FLOAT_EQ(ev.time(8 ), 2    );   EXPECT_EQ(ev.evid(8 ), 0);
    EXPECT_FLOAT_EQ(ev.time(9 ), 3.75 );   EXPECT_EQ(ev.evid(9 ), 2);
    EXPECT_FLOAT_EQ(ev.time(10), 4    );   EXPECT_EQ(ev.evid(10), 0);
    EXPECT_FLOAT_EQ(ev.time(11), 5.75 );   EXPECT_EQ(ev.evid(11), 1);
    EXPECT_FLOAT_EQ(ev.time(12), 8.75 );   EXPECT_EQ(ev.evid(12), 2);
    EXPECT_FLOAT_EQ(ev.time(13), 10.75);   EXPECT_EQ(ev.evid(13), 1);
    EXPECT_FLOAT_EQ(ev.time(14), 12   );   EXPECT_EQ(ev.evid(14), 1);
    EXPECT_FLOAT_EQ(ev.time(15), 13.75);   EXPECT_EQ(ev.evid(15), 2);
    EXPECT_FLOAT_EQ(ev.time(16), 24   );   EXPECT_EQ(ev.evid(16), 1);
    EXPECT_FLOAT_EQ(ev.time(17), 36   );   EXPECT_EQ(ev.evid(17), 1);
    EXPECT_FLOAT_EQ(ev.time(18), 48   );   EXPECT_EQ(ev.evid(18), 1);
    EXPECT_FLOAT_EQ(ev.time(19), 60   );   EXPECT_EQ(ev.evid(19), 1);

    EXPECT_FLOAT_EQ(ev.fractioned_rates(0 )[1], 0  );
    EXPECT_FLOAT_EQ(ev.fractioned_rates(1 )[1], 0  );
    EXPECT_FLOAT_EQ(ev.fractioned_rates(2 )[1], 0  );
    EXPECT_FLOAT_EQ(ev.fractioned_rates(3 )[1], 0  );
    EXPECT_FLOAT_EQ(ev.fractioned_rates(4 )[1], 400);
    EXPECT_FLOAT_EQ(ev.fractioned_rates(5 )[1], 400);
    EXPECT_FLOAT_EQ(ev.fractioned_rates(6 )[1], 400);
    EXPECT_FLOAT_EQ(ev.fractioned_rates(7 )[1], 400);
    EXPECT_FLOAT_EQ(ev.fractioned_rates(8 )[1], 400);
    EXPECT_FLOAT_EQ(ev.fractioned_rates(9 )[1], 400);
    EXPECT_FLOAT_EQ(ev.fractioned_rates(10)[1], 0  );
    EXPECT_FLOAT_EQ(ev.fractioned_rates(11)[1], 0  );
    EXPECT_FLOAT_EQ(ev.fractioned_rates(12)[1], 400);
    EXPECT_FLOAT_EQ(ev.fractioned_rates(13)[1], 0  );
    EXPECT_FLOAT_EQ(ev.fractioned_rates(14)[1], 400);
    EXPECT_FLOAT_EQ(ev.fractioned_rates(15)[1], 400);
    EXPECT_FLOAT_EQ(ev.fractioned_rates(16)[1], 0  );
    EXPECT_FLOAT_EQ(ev.fractioned_rates(17)[1], 0  );
    EXPECT_FLOAT_EQ(ev.fractioned_rates(18)[1], 0  );
    EXPECT_FLOAT_EQ(ev.fractioned_rates(19)[1], 0  );
  }

{
    EM em(1, events_rec, n1, n1, n2, n2, n3, n3);
    auto ev = em.events();
    EXPECT_EQ(ev.num_state_times(), EM::num_events(1, events_rec) );
    EXPECT_FLOAT_EQ(ev.time(0 ), 0    );   EXPECT_EQ(ev.evid(0 ), 1);
    EXPECT_FLOAT_EQ(ev.time(1 ), 0.25 );   EXPECT_EQ(ev.evid(1 ), 0);
    EXPECT_FLOAT_EQ(ev.time(2 ), 0.5  );   EXPECT_EQ(ev.evid(2 ), 0);
    EXPECT_FLOAT_EQ(ev.time(3 ), 0.75 );   EXPECT_EQ(ev.evid(3 ), 1);
    EXPECT_FLOAT_EQ(ev.time(4 ), 1    );   EXPECT_EQ(ev.evid(4 ), 0);
    EXPECT_FLOAT_EQ(ev.time(5 ), 1.25 );   EXPECT_EQ(ev.evid(5 ), 0);
    EXPECT_FLOAT_EQ(ev.time(6 ), 1.5  );   EXPECT_EQ(ev.evid(6 ), 0);
    EXPECT_FLOAT_EQ(ev.time(7 ), 1.75 );   EXPECT_EQ(ev.evid(7 ), 0);
    EXPECT_FLOAT_EQ(ev.time(8 ), 2    );   EXPECT_EQ(ev.evid(8 ), 0);
    EXPECT_FLOAT_EQ(ev.time(9 ), 3.75 );   EXPECT_EQ(ev.evid(9 ), 2);
    EXPECT_FLOAT_EQ(ev.time(10), 4    );   EXPECT_EQ(ev.evid(10), 0);
    EXPECT_FLOAT_EQ(ev.time(11), 5.75 );   EXPECT_EQ(ev.evid(11), 1);
    EXPECT_FLOAT_EQ(ev.time(12), 8.75 );   EXPECT_EQ(ev.evid(12), 2);
    EXPECT_FLOAT_EQ(ev.time(13), 10.75);   EXPECT_EQ(ev.evid(13), 1);
    EXPECT_FLOAT_EQ(ev.time(14), 12   );   EXPECT_EQ(ev.evid(14), 1);
    EXPECT_FLOAT_EQ(ev.time(15), 13.75);   EXPECT_EQ(ev.evid(15), 2);
    EXPECT_FLOAT_EQ(ev.time(16), 24   );   EXPECT_EQ(ev.evid(16), 1);
    EXPECT_FLOAT_EQ(ev.time(17), 36   );   EXPECT_EQ(ev.evid(17), 1);
    EXPECT_FLOAT_EQ(ev.time(18), 48   );   EXPECT_EQ(ev.evid(18), 1);
    EXPECT_FLOAT_EQ(ev.time(19), 60   );   EXPECT_EQ(ev.evid(19), 1);

    EXPECT_FLOAT_EQ(ev.fractioned_rates(0 )[1], 0  );
    EXPECT_FLOAT_EQ(ev.fractioned_rates(1 )[1], 0  );
    EXPECT_FLOAT_EQ(ev.fractioned_rates(2 )[1], 0  );
    EXPECT_FLOAT_EQ(ev.fractioned_rates(3 )[1], 0  );
    EXPECT_FLOAT_EQ(ev.fractioned_rates(4 )[1], 400);
    EXPECT_FLOAT_EQ(ev.fractioned_rates(5 )[1], 400);
    EXPECT_FLOAT_EQ(ev.fractioned_rates(6 )[1], 400);
    EXPECT_FLOAT_EQ(ev.fractioned_rates(7 )[1], 400);
    EXPECT_FLOAT_EQ(ev.fractioned_rates(8 )[1], 400);
    EXPECT_FLOAT_EQ(ev.fractioned_rates(9 )[1], 400);
    EXPECT_FLOAT_EQ(ev.fractioned_rates(10)[1], 0  );
    EXPECT_FLOAT_EQ(ev.fractioned_rates(11)[1], 0  );
    EXPECT_FLOAT_EQ(ev.fractioned_rates(12)[1], 400);
    EXPECT_FLOAT_EQ(ev.fractioned_rates(13)[1], 0  );
    EXPECT_FLOAT_EQ(ev.fractioned_rates(14)[1], 400);
    EXPECT_FLOAT_EQ(ev.fractioned_rates(15)[1], 400);
    EXPECT_FLOAT_EQ(ev.fractioned_rates(16)[1], 0  );
    EXPECT_FLOAT_EQ(ev.fractioned_rates(17)[1], 0  );
    EXPECT_FLOAT_EQ(ev.fractioned_rates(18)[1], 0  );
    EXPECT_FLOAT_EQ(ev.fractioned_rates(19)[1], 0  );
  }
}

TEST_F(TorstenTwoCptTest, events_addl_const_tlag) {
  using ER = NONMENEventsRecord<double, double, double, double, std::vector<double>, double, double>;
  using EM = EventsManager<ER>;

  int nCmt = refactor::PMXTwoCptModel<double, double, double, double>::Ncmt;
  addl[0] = 1;
  addl[3] = 2;
  amt[3] = 1200.0;
  ii[3] = 3;
  cmt[3] = 2;
  evid[3] = 4;

  // lag for cmt 1, this shouldn't affect the dose on cmt 2.
  tlag[0][0] = 0.25;
  {
    ER events_rec(nCmt, time, amt, rate, ii, evid, cmt, addl, ss, pMatrix, biovar, tlag);
    EM em(events_rec);
    auto ev = em.events();
    EXPECT_EQ(ev.num_state_times(), time.size() + addl[0] * 2 + 1 + addl[3]);
    EXPECT_EQ(ev.num_state_times(), EM::num_events(events_rec) );

    EXPECT_FLOAT_EQ(ev.time(0 ), 0    ); EXPECT_FLOAT_EQ(ev.amt(0 ), 1000.);
    EXPECT_FLOAT_EQ(ev.time(1 ), 0.25 ); EXPECT_FLOAT_EQ(ev.amt(1 ), 0.   );
    EXPECT_FLOAT_EQ(ev.time(2 ), 0.25 ); EXPECT_FLOAT_EQ(ev.amt(2 ), 1000.);
    EXPECT_FLOAT_EQ(ev.time(3 ), 0.5  ); EXPECT_FLOAT_EQ(ev.amt(3 ), 0.   );
    EXPECT_FLOAT_EQ(ev.time(4 ), 0.75 ); EXPECT_FLOAT_EQ(ev.amt(4 ), 1200.);
    EXPECT_FLOAT_EQ(ev.time(5 ), 1    ); EXPECT_FLOAT_EQ(ev.amt(5 ), 0.   );
    EXPECT_FLOAT_EQ(ev.time(6 ), 1.25 ); EXPECT_FLOAT_EQ(ev.amt(6 ), 0.   );
    EXPECT_FLOAT_EQ(ev.time(7 ), 1.5  ); EXPECT_FLOAT_EQ(ev.amt(7 ), 0.   );
    EXPECT_FLOAT_EQ(ev.time(8 ), 1.75 ); EXPECT_FLOAT_EQ(ev.amt(8 ), 0.   );
    EXPECT_FLOAT_EQ(ev.time(9 ), 2    ); EXPECT_FLOAT_EQ(ev.amt(9 ), 0.   );
    EXPECT_FLOAT_EQ(ev.time(10), 3.75 ); EXPECT_FLOAT_EQ(ev.amt(10), 1200.);
    EXPECT_FLOAT_EQ(ev.time(11), 4    ); EXPECT_FLOAT_EQ(ev.amt(11), 0.   );
    EXPECT_FLOAT_EQ(ev.time(12), 6.75 ); EXPECT_FLOAT_EQ(ev.amt(12), 1200.);
    EXPECT_FLOAT_EQ(ev.time(13), 12   ); EXPECT_FLOAT_EQ(ev.amt(13), 1000.);
    EXPECT_FLOAT_EQ(ev.time(14), 12.25); EXPECT_FLOAT_EQ(ev.amt(14), 1000.);

    EXPECT_EQ(ev.cmt(0 ), 1);     EXPECT_EQ(ev.evid(0 ), 2);
    EXPECT_EQ(ev.cmt(1 ), 3);     EXPECT_EQ(ev.evid(1 ), 0);
    EXPECT_EQ(ev.cmt(2 ), 1);     EXPECT_EQ(ev.evid(2 ), 1);
    EXPECT_EQ(ev.cmt(3 ), 3);     EXPECT_EQ(ev.evid(3 ), 0);
    EXPECT_EQ(ev.cmt(4 ), 2);     EXPECT_EQ(ev.evid(4 ), 4);
    EXPECT_EQ(ev.cmt(5 ), 3);     EXPECT_EQ(ev.evid(5 ), 0);
    EXPECT_EQ(ev.cmt(6 ), 3);     EXPECT_EQ(ev.evid(6 ), 0);
    EXPECT_EQ(ev.cmt(7 ), 3);     EXPECT_EQ(ev.evid(7 ), 0);
    EXPECT_EQ(ev.cmt(8 ), 3);     EXPECT_EQ(ev.evid(8 ), 0);
    EXPECT_EQ(ev.cmt(9 ), 3);     EXPECT_EQ(ev.evid(9 ), 0);
    EXPECT_EQ(ev.cmt(10), 2);     EXPECT_EQ(ev.evid(10), 4);
    EXPECT_EQ(ev.cmt(11), 3);     EXPECT_EQ(ev.evid(11), 0);
    EXPECT_EQ(ev.cmt(12), 2);     EXPECT_EQ(ev.evid(12), 4);
    EXPECT_EQ(ev.cmt(13), 1);     EXPECT_EQ(ev.evid(13), 2);
    EXPECT_EQ(ev.cmt(14), 1);     EXPECT_EQ(ev.evid(14), 1);
  }

  // lag for cmt 2, this shouldn't affect the dose on cmt 1.
  tlag[0][0] = 0.0;
  tlag[0][1] = 0.2;
  {
    ER events_rec(nCmt, time, amt, rate, ii, evid, cmt, addl, ss, pMatrix, biovar, tlag);
    EM em(events_rec);
    auto ev = em.events();
    EXPECT_EQ(ev.num_state_times(), time.size() + addl[3] * 2 + 1 + addl[0]);
    EXPECT_EQ(ev.num_state_times(), EM::num_events(events_rec) );

    EXPECT_FLOAT_EQ(ev.time(0 ), 0   );   EXPECT_FLOAT_EQ(ev.amt(0 ), 1000.);
    EXPECT_FLOAT_EQ(ev.time(1 ), 0.25);   EXPECT_FLOAT_EQ(ev.amt(1 ), 0.   );
    EXPECT_FLOAT_EQ(ev.time(2 ), 0.5 );   EXPECT_FLOAT_EQ(ev.amt(2 ), 0.   );
    EXPECT_FLOAT_EQ(ev.time(3 ), 0.75);   EXPECT_FLOAT_EQ(ev.amt(3 ), 1200.);
    EXPECT_FLOAT_EQ(ev.time(4 ), 0.95);   EXPECT_FLOAT_EQ(ev.amt(4 ), 1200.);
    EXPECT_FLOAT_EQ(ev.time(5 ), 1   );   EXPECT_FLOAT_EQ(ev.amt(5 ), 0.   );
    EXPECT_FLOAT_EQ(ev.time(6 ), 1.25);   EXPECT_FLOAT_EQ(ev.amt(6 ), 0.   );
    EXPECT_FLOAT_EQ(ev.time(7 ), 1.5 );   EXPECT_FLOAT_EQ(ev.amt(7 ), 0.   );
    EXPECT_FLOAT_EQ(ev.time(8 ), 1.75);   EXPECT_FLOAT_EQ(ev.amt(8 ), 0.   );
    EXPECT_FLOAT_EQ(ev.time(9 ), 2   );   EXPECT_FLOAT_EQ(ev.amt(9 ), 0.   );
    EXPECT_FLOAT_EQ(ev.time(10), 3.75);   EXPECT_FLOAT_EQ(ev.amt(10), 1200.);
    EXPECT_FLOAT_EQ(ev.time(11), 3.95);   EXPECT_FLOAT_EQ(ev.amt(11), 1200.);
    EXPECT_FLOAT_EQ(ev.time(12), 4   );   EXPECT_FLOAT_EQ(ev.amt(12), 0.   );
    EXPECT_FLOAT_EQ(ev.time(13), 6.75);   EXPECT_FLOAT_EQ(ev.amt(13), 1200.);
    EXPECT_FLOAT_EQ(ev.time(14), 6.95);   EXPECT_FLOAT_EQ(ev.amt(14), 1200.);
    EXPECT_FLOAT_EQ(ev.time(15), 12  );   EXPECT_FLOAT_EQ(ev.amt(15), 1000.);

    EXPECT_EQ(ev.cmt(0 ), 1);     EXPECT_EQ(ev.evid(0 ), 1);
    EXPECT_EQ(ev.cmt(1 ), 3);     EXPECT_EQ(ev.evid(1 ), 0);
    EXPECT_EQ(ev.cmt(2 ), 3);     EXPECT_EQ(ev.evid(2 ), 0);
    EXPECT_EQ(ev.cmt(3 ), 2);     EXPECT_EQ(ev.evid(3 ), 2);
    EXPECT_EQ(ev.cmt(4 ), 2);     EXPECT_EQ(ev.evid(4 ), 4);
    EXPECT_EQ(ev.cmt(5 ), 3);     EXPECT_EQ(ev.evid(5 ), 0);
    EXPECT_EQ(ev.cmt(6 ), 3);     EXPECT_EQ(ev.evid(6 ), 0);
    EXPECT_EQ(ev.cmt(7 ), 3);     EXPECT_EQ(ev.evid(7 ), 0);
    EXPECT_EQ(ev.cmt(8 ), 3);     EXPECT_EQ(ev.evid(8 ), 0);
    EXPECT_EQ(ev.cmt(9 ), 3);     EXPECT_EQ(ev.evid(9 ), 0);
    EXPECT_EQ(ev.cmt(10), 2);     EXPECT_EQ(ev.evid(10), 2);
    EXPECT_EQ(ev.cmt(11), 2);     EXPECT_EQ(ev.evid(11), 4);
    EXPECT_EQ(ev.cmt(12), 3);     EXPECT_EQ(ev.evid(12), 0);
    EXPECT_EQ(ev.cmt(13), 2);     EXPECT_EQ(ev.evid(13), 2);
    EXPECT_EQ(ev.cmt(14), 2);     EXPECT_EQ(ev.evid(14), 4);
    EXPECT_EQ(ev.cmt(15), 1);     EXPECT_EQ(ev.evid(15), 1);
  }
}

TEST_F(TorstenTwoCptTest, events_addl_rate_const_tlag) {
  using ER = NONMENEventsRecord<double, double, double, double, std::vector<double>, double, double>;
  using EM = EventsManager<ER>;

  int nCmt = refactor::PMXTwoCptModel<double, double, double, double>::Ncmt;
  addl[0] = 1;
  addl[3] = 2;
  amt[3] = 1200.0;
  ii[3] = 5;
  cmt[3] = 2;
  rate[3] = 400;
  evid[3] = 1;

  tlag[0][1] = 0.25;
  ER events_rec(nCmt, time, amt, rate, ii, evid, cmt, addl, ss, pMatrix, biovar, tlag);
  EM em(events_rec);
  auto ev = em.events();
  EXPECT_EQ(ev.num_state_times(), time.size() + addl[3] * 3 + 2 + addl[0]);
  EXPECT_EQ(ev.num_state_times(), EM::num_events(events_rec) );

  EXPECT_FLOAT_EQ(ev.time(0 ), 0    ); EXPECT_FLOAT_EQ(ev.fractioned_rates(0 )[1], 0.  );
  EXPECT_FLOAT_EQ(ev.time(1 ), 0.25 ); EXPECT_FLOAT_EQ(ev.fractioned_rates(1 )[1], 0.  );
  EXPECT_FLOAT_EQ(ev.time(2 ), 0.5  ); EXPECT_FLOAT_EQ(ev.fractioned_rates(2 )[1], 0.  );
  EXPECT_FLOAT_EQ(ev.time(3 ), 0.75 ); EXPECT_FLOAT_EQ(ev.fractioned_rates(3 )[1], 0.  );
  EXPECT_FLOAT_EQ(ev.time(4 ), 1    ); EXPECT_FLOAT_EQ(ev.fractioned_rates(4 )[1], 0.  );
  EXPECT_FLOAT_EQ(ev.time(5 ), 1    ); EXPECT_FLOAT_EQ(ev.fractioned_rates(5 )[1], 0.  );
  EXPECT_FLOAT_EQ(ev.time(6 ), 1.25 ); EXPECT_FLOAT_EQ(ev.fractioned_rates(6 )[1], 400.);
  EXPECT_FLOAT_EQ(ev.time(7 ), 1.5  ); EXPECT_FLOAT_EQ(ev.fractioned_rates(7 )[1], 400.);
  EXPECT_FLOAT_EQ(ev.time(8 ), 1.75 ); EXPECT_FLOAT_EQ(ev.fractioned_rates(8 )[1], 400.);
  EXPECT_FLOAT_EQ(ev.time(9 ), 2    ); EXPECT_FLOAT_EQ(ev.fractioned_rates(9 )[1], 400.);
  EXPECT_FLOAT_EQ(ev.time(10), 4    ); EXPECT_FLOAT_EQ(ev.fractioned_rates(10)[1], 400.);
  EXPECT_FLOAT_EQ(ev.time(11), 4    ); EXPECT_FLOAT_EQ(ev.fractioned_rates(11)[1], 400.);
  EXPECT_FLOAT_EQ(ev.time(12), 5.75 ); EXPECT_FLOAT_EQ(ev.fractioned_rates(12)[1], 0.  );
  EXPECT_FLOAT_EQ(ev.time(13), 6    ); EXPECT_FLOAT_EQ(ev.fractioned_rates(13)[1], 0.  );
  EXPECT_FLOAT_EQ(ev.time(14), 9    ); EXPECT_FLOAT_EQ(ev.fractioned_rates(14)[1], 400.);
  EXPECT_FLOAT_EQ(ev.time(15), 10.75); EXPECT_FLOAT_EQ(ev.fractioned_rates(15)[1], 0.  );
  EXPECT_FLOAT_EQ(ev.time(16), 11   ); EXPECT_FLOAT_EQ(ev.fractioned_rates(16)[1], 0.  );
  EXPECT_FLOAT_EQ(ev.time(17), 12   ); EXPECT_FLOAT_EQ(ev.fractioned_rates(17)[1], 400.);
  EXPECT_FLOAT_EQ(ev.time(18), 14   ); EXPECT_FLOAT_EQ(ev.fractioned_rates(18)[1], 400.);

  EXPECT_EQ(ev.evid(0 ), 1);  EXPECT_EQ(ev.cmt(0 ), 1);
  EXPECT_EQ(ev.evid(1 ), 0);  EXPECT_EQ(ev.cmt(1 ), 3);
  EXPECT_EQ(ev.evid(2 ), 0);  EXPECT_EQ(ev.cmt(2 ), 3);
  EXPECT_EQ(ev.evid(3 ), 2);  EXPECT_EQ(ev.cmt(3 ), 2);
  EXPECT_EQ(ev.evid(4 ), 0);  EXPECT_EQ(ev.cmt(4 ), 3);
  EXPECT_EQ(ev.evid(5 ), 1);  EXPECT_EQ(ev.cmt(5 ), 2);
  EXPECT_EQ(ev.evid(6 ), 0);  EXPECT_EQ(ev.cmt(6 ), 3);
  EXPECT_EQ(ev.evid(7 ), 0);  EXPECT_EQ(ev.cmt(7 ), 3);
  EXPECT_EQ(ev.evid(8 ), 0);  EXPECT_EQ(ev.cmt(8 ), 3);
  EXPECT_EQ(ev.evid(9 ), 0);  EXPECT_EQ(ev.cmt(9 ), 3);
  EXPECT_EQ(ev.evid(10), 0);  EXPECT_EQ(ev.cmt(10), 3);
  EXPECT_EQ(ev.evid(11), 2);  EXPECT_EQ(ev.cmt(11), 2);
  EXPECT_EQ(ev.evid(12), 2);  EXPECT_EQ(ev.cmt(12), 2);
  EXPECT_EQ(ev.evid(13), 1);  EXPECT_EQ(ev.cmt(13), 2);
  EXPECT_EQ(ev.evid(14), 2);  EXPECT_EQ(ev.cmt(14), 2);
  EXPECT_EQ(ev.evid(15), 2);  EXPECT_EQ(ev.cmt(15), 2);
  EXPECT_EQ(ev.evid(16), 1);  EXPECT_EQ(ev.cmt(16), 2);
  EXPECT_EQ(ev.evid(17), 1);  EXPECT_EQ(ev.cmt(17), 1);
  EXPECT_EQ(ev.evid(18), 2);  EXPECT_EQ(ev.cmt(18), 2);
}

TEST_F(TorstenPopulationPMXTwoCptTest, events_addl_rate_const_tlag) {
  using ER = NONMENEventsRecord<double, double, double, double, std::vector<double>, double, double>;
  using EM = EventsManager<ER>;

  int nCmt = refactor::PMXTwoCptModel<double, double, double, double>::Ncmt;
  int id = 1;
  addl_m[id * nt + 0] = 1;
  addl_m[id * nt + 3] = 2;
  amt_m[id * nt + 3] = 1200.0;
  ii_m[id * nt + 3] = 5;
  cmt_m[id * nt + 3] = 2;
  rate_m[id * nt + 3] = 400;
  evid_m[id * nt + 3] = 1;
  tlag_m[id * 1][1] = 0.25;

  id = 5;
  addl_m[id * nt + 3] = 1;
  amt_m[id * nt + 3] = 1000.0;
  ii_m[id * nt + 3] = 5;
  cmt_m[id * nt + 3] = 2;
  rate_m[id * nt + 3] = 100;
  evid_m[id * nt + 3] = 1;
  tlag_m[id * 1][1] = 0.12;

  std::vector<int> len(np, time.size());
  ER all_events_rec(nCmt, len, time_m, amt_m, rate_m, ii_m, evid_m, cmt_m, addl_m, ss_m, pMatrix_m, biovar_m, tlag_m);

  int ibegin = 0, ibegin_pMatrix = 0, ibegin_biovar = 0, ibegin_tlag = 0;
  int isize = 0, isize_pMatrix = 0, isize_biovar = 0, isize_tlag = 0;
  for (int id = 0; id < np; ++id) {
    ibegin += isize;
    ibegin_pMatrix += isize_pMatrix;
    ibegin_biovar += isize_biovar;
    ibegin_tlag += isize_tlag;
    isize = len[id];
    isize_pMatrix = len_pMatrix[id];
    isize_biovar = len_biovar[id];
    isize_tlag = len_tlag[id];

    std::vector<double> time_i(time_m.begin() + ibegin, time_m.begin() + ibegin + isize);
    std::vector<double> amt_i(amt_m.begin() + ibegin, amt_m.begin() + ibegin + isize);
    std::vector<double> rate_i(rate_m.begin() + ibegin, rate_m.begin() + ibegin + isize);
    std::vector<double> ii_i(ii_m.begin() + ibegin, ii_m.begin() + ibegin + isize);
    std::vector<int> evid_i(evid_m.begin() + ibegin, evid_m.begin() + ibegin + isize);
    std::vector<int> cmt_i(cmt_m.begin() + ibegin, cmt_m.begin() + ibegin + isize);
    std::vector<int> addl_i(addl_m.begin() + ibegin, addl_m.begin() + ibegin + isize);
    std::vector<int> ss_i(ss_m.begin() + ibegin, ss_m.begin() + ibegin + isize);
    std::vector<std::vector<double> > pMatrix_i(pMatrix_m.begin() + ibegin_pMatrix, pMatrix_m.begin() + ibegin_pMatrix + isize_pMatrix);
    std::vector<std::vector<double> > biovar_i(biovar_m.begin() + ibegin_biovar, biovar_m.begin() + ibegin_biovar + isize_biovar);
    std::vector<std::vector<double> > tlag_i(tlag_m.begin() + ibegin_tlag, tlag_m.begin() + ibegin_tlag + isize_tlag);

    ER events_rec(nCmt, time_i, amt_i, rate_i, ii_i, evid_i, cmt_i, addl_i, ss_i, pMatrix_i, biovar_i, tlag_i);
    EM em(events_rec);
    EM em_i(id, all_events_rec, ibegin_pMatrix, isize_pMatrix, ibegin_biovar, isize_biovar, ibegin_tlag, isize_tlag);
    EXPECT_EQ(em.events().num_state_times(), em_i.events().num_state_times());
    EXPECT_EQ(em.events().num_state_times(), EM::num_events(id, all_events_rec) );
    for (size_t j = 0; j < em.events().num_state_times(); ++j) {
      EXPECT_FLOAT_EQ(em.events().time(j), em_i.events().time(j));
      EXPECT_FLOAT_EQ(em.events().amt(j), em_i.events().amt(j));
      EXPECT_FLOAT_EQ(em.events().fractioned_rates(j)[1], em_i.events().fractioned_rates(j)[1]);
      EXPECT_EQ(em.events().evid(j), em_i.events().evid(j));
    }
  }
}

TEST_F(TorstenTwoCptTest, events_addl_rate_tlag) {
  using ER = NONMENEventsRecord<double, double, double, double, std::vector<double>, double, double>;
  using EM = EventsManager<ER>;

  int nCmt = refactor::PMXTwoCptModel<double, double, double, double>::Ncmt;
  addl[0] = 0;
  addl[3] = 2;
  amt[3] = 1200.0;
  ii[3] = 5;
  cmt[3] = 2;
  rate[3] = 400;
  evid[3] = 1;

  tlag.resize(time.size());
  for (auto& l : tlag) l.resize(nCmt);
  tlag[3][1] = 0.25;

  ER events_rec(nCmt, time, amt, rate, ii, evid, cmt, addl, ss, pMatrix, biovar, tlag);
  EM em(events_rec);
  auto ev = em.events();

  // Last Observation Carried Forward for the parameters for
  // the addl events. Since the last param has no lags, the
  // addl events have no lags.
  EXPECT_EQ(ev.num_state_times(), time.size() + addl[3] * 2 + 2);

  EXPECT_FLOAT_EQ(ev.time(0 ), 0    ); EXPECT_FLOAT_EQ(ev.fractioned_rates(0 )[1],    0.0);
  EXPECT_FLOAT_EQ(ev.time(1 ), 0.25 ); EXPECT_FLOAT_EQ(ev.fractioned_rates(1 )[1],    0.0);
  EXPECT_FLOAT_EQ(ev.time(2 ), 0.5  ); EXPECT_FLOAT_EQ(ev.fractioned_rates(2 )[1],    0.0);
  EXPECT_FLOAT_EQ(ev.time(3 ), 0.75 ); EXPECT_FLOAT_EQ(ev.fractioned_rates(3 )[1],    0.0);
  EXPECT_FLOAT_EQ(ev.time(4 ), 1    ); EXPECT_FLOAT_EQ(ev.fractioned_rates(4 )[1],    0.0);
  EXPECT_FLOAT_EQ(ev.time(5 ), 1    ); EXPECT_FLOAT_EQ(ev.fractioned_rates(5 )[1],    0.0);
  EXPECT_FLOAT_EQ(ev.time(6 ), 1.25 ); EXPECT_FLOAT_EQ(ev.fractioned_rates(6 )[1],  400.0);
  EXPECT_FLOAT_EQ(ev.time(7 ), 1.5  ); EXPECT_FLOAT_EQ(ev.fractioned_rates(7 )[1],  400.0);
  EXPECT_FLOAT_EQ(ev.time(8 ), 1.75 ); EXPECT_FLOAT_EQ(ev.fractioned_rates(8 )[1],  400.0);
  EXPECT_FLOAT_EQ(ev.time(9 ), 2    ); EXPECT_FLOAT_EQ(ev.fractioned_rates(9 )[1],  400.0);
  EXPECT_FLOAT_EQ(ev.time(10), 4    ); EXPECT_FLOAT_EQ(ev.fractioned_rates(10)[1],  400.0);
  EXPECT_FLOAT_EQ(ev.time(11), 4    ); EXPECT_FLOAT_EQ(ev.fractioned_rates(11)[1],  400.0);
  EXPECT_FLOAT_EQ(ev.time(12), 5.75 ); EXPECT_FLOAT_EQ(ev.fractioned_rates(12)[1],    0.0);
  EXPECT_FLOAT_EQ(ev.time(13), 8.75 ); EXPECT_FLOAT_EQ(ev.fractioned_rates(13)[1],  400.0);
  EXPECT_FLOAT_EQ(ev.time(14), 10.75); EXPECT_FLOAT_EQ(ev.fractioned_rates(14)[1],    0.0);
  EXPECT_FLOAT_EQ(ev.time(15), 13.75); EXPECT_FLOAT_EQ(ev.fractioned_rates(15)[1],  400.0);
}

TEST_F(TorstenTwoCptTest, events_addl_rate_tlag_LOCF) {
  using ER = NONMENEventsRecord<double, double, double, double, std::vector<double>, double, double>;
  using EM = EventsManager<ER>;

  int nCmt = refactor::PMXTwoCptModel<double, double, double, double>::Ncmt;
  addl[0] = 0;
  addl[3] = 2;
  amt[3] = 1200.0;
  ii[3] = 2.1;
  cmt[3] = 2;
  rate[3] = 400;
  evid[3] = 1;
  time[time.size() - 2] = 3.0;

  tlag.resize(time.size());
  for (auto& l : tlag) l.resize(nCmt);
  tlag[time.size() - 1][1] = 0.25;

  ER events_rec(nCmt, time, amt, rate, ii, evid, cmt, addl, ss, pMatrix, biovar, tlag);
  EM em(events_rec);
  auto ev = em.events();

  // Last Observation Carried Forward for the parameters for
  // the addl events. Since the last param has a lag, the
  // addl events that are after it have a lag. Since the
  // first of the two addl dosing event is at t = 2.85, and
  // it's a time that are not in original time points, the
  // parameter for this event is the one of the subsequent
  // event, which has no lag. Therefore the 1st addl event
  // has no lag, but the 2nd has.
  EXPECT_EQ(ev.num_state_times(), time.size() + 6);

  EXPECT_FLOAT_EQ(ev.time(0 ), 0   ); EXPECT_FLOAT_EQ(ev.fractioned_rates(0 )[1], 0  );
  EXPECT_FLOAT_EQ(ev.time(1 ), 0.25); EXPECT_FLOAT_EQ(ev.fractioned_rates(1 )[1], 0  );
  EXPECT_FLOAT_EQ(ev.time(2 ), 0.5 ); EXPECT_FLOAT_EQ(ev.fractioned_rates(2 )[1], 0  );
  EXPECT_FLOAT_EQ(ev.time(3 ), 0.75); EXPECT_FLOAT_EQ(ev.fractioned_rates(3 )[1], 0  );
  EXPECT_FLOAT_EQ(ev.time(4 ), 1   ); EXPECT_FLOAT_EQ(ev.fractioned_rates(4 )[1], 400);
  EXPECT_FLOAT_EQ(ev.time(5 ), 1.25); EXPECT_FLOAT_EQ(ev.fractioned_rates(5 )[1], 400);
  EXPECT_FLOAT_EQ(ev.time(6 ), 1.5 ); EXPECT_FLOAT_EQ(ev.fractioned_rates(6 )[1], 400);
  EXPECT_FLOAT_EQ(ev.time(7 ), 1.75); EXPECT_FLOAT_EQ(ev.fractioned_rates(7 )[1], 400);
  EXPECT_FLOAT_EQ(ev.time(8 ), 2.85); EXPECT_FLOAT_EQ(ev.fractioned_rates(8 )[1], 400);
  EXPECT_FLOAT_EQ(ev.time(9 ), 3   ); EXPECT_FLOAT_EQ(ev.fractioned_rates(9 )[1], 800);
  EXPECT_FLOAT_EQ(ev.time(10), 3.75); EXPECT_FLOAT_EQ(ev.fractioned_rates(10)[1], 800);
  EXPECT_FLOAT_EQ(ev.time(11), 4   ); EXPECT_FLOAT_EQ(ev.fractioned_rates(11)[1], 400);
  EXPECT_FLOAT_EQ(ev.time(12), 4.95); EXPECT_FLOAT_EQ(ev.fractioned_rates(12)[1], 400);
  EXPECT_FLOAT_EQ(ev.time(13), 5.2 ); EXPECT_FLOAT_EQ(ev.fractioned_rates(13)[1], 400);
  EXPECT_FLOAT_EQ(ev.time(14), 5.85); EXPECT_FLOAT_EQ(ev.fractioned_rates(14)[1], 800);
  EXPECT_FLOAT_EQ(ev.time(15), 8.2 ); EXPECT_FLOAT_EQ(ev.fractioned_rates(15)[1], 400);

  EXPECT_EQ(ev.evid(0 ), 1);
  EXPECT_EQ(ev.evid(1 ), 0);
  EXPECT_EQ(ev.evid(2 ), 0);
  EXPECT_EQ(ev.evid(3 ), 1);
  EXPECT_EQ(ev.evid(4 ), 0);
  EXPECT_EQ(ev.evid(5 ), 0);
  EXPECT_EQ(ev.evid(6 ), 0);
  EXPECT_EQ(ev.evid(7 ), 0);
  EXPECT_EQ(ev.evid(8 ), 1);
  EXPECT_EQ(ev.evid(9 ), 0);
  EXPECT_EQ(ev.evid(10), 2);
  EXPECT_EQ(ev.evid(11), 0);
  EXPECT_EQ(ev.evid(12), 2);
  EXPECT_EQ(ev.evid(13), 1);
  EXPECT_EQ(ev.evid(14), 2);
  EXPECT_EQ(ev.evid(15), 2);
}

TEST_F(TorstenPopulationPMXTwoCptTest, events_addl_rate_tlag_LOCF) {
  using ER = NONMENEventsRecord<double, double, double, double, std::vector<double>, double, double>;
  using EM = EventsManager<ER>;

  int nCmt = refactor::PMXTwoCptModel<double, double, double, double>::Ncmt;
  int id = 1;
  addl_m[id * nt + 0] = 0;
  addl_m[id * nt + 3] = 2;
  amt_m[id * nt + 3] = 1200.0;
  ii_m[id * nt + 3] = 2.1;
  cmt_m[id * nt + 3] = 2;
  rate_m[id * nt + 3] = 400;
  evid_m[id * nt + 3] = 1;
  time_m[id * nt + time.size() - 2] = 3.0;

  id  = 5;
  addl_m[id * nt + 0] = 0;
  addl_m[id * nt + 3] = 2;
  amt_m[id * nt + 3] = 1000.0;
  ii_m[id * nt + 3] = 2.1;
  cmt_m[id * nt + 3] = 2;
  rate_m[id * nt + 3] = 400;
  evid_m[id * nt + 3] = 1;
  time_m[id * nt + time.size() - 2] = 3.0;

  // tlag now contains both constant data and time-dependent
  // data(for id 1 & 5)
  tlag_m.clear();
  for (int id = 0; id < np; ++id) {
    if (id == 1) {
      for (int j = 0; j < nt; ++j) {
        std::vector<double> tlag_i(nCmt, 0.0);
        if (j == len[id] - 1) tlag_i[1] = 0.25;
        tlag_m.push_back(tlag_i);
      }
      len_tlag[id] = nt;
    } else if (id == 5) {
      for (int j = 0; j < nt; ++j) {
        std::vector<double> tlag_i(nCmt, 0.0);
        if (j == len[id] - 1) tlag_i[1] = 0.13;
        tlag_m.push_back(tlag_i);
      }
      len_tlag[id] = nt;
    } else {
      std::vector<double> tlag_i(nCmt, 0.0);
      tlag_m.push_back(tlag_i);
      len_tlag[id] = 1;
    }
  }

  std::vector<int> len(np, time.size());
  ER all_events_rec(nCmt, len, time_m, amt_m, rate_m, ii_m, evid_m, cmt_m, addl_m, ss_m, pMatrix_m, biovar_m, tlag_m);

  int ibegin = 0, ibegin_pMatrix = 0, ibegin_biovar = 0, ibegin_tlag = 0;
  int isize = 0, isize_pMatrix = 0, isize_biovar = 0, isize_tlag = 0;
  for (int id = 0; id < np; ++id) {
    ibegin += isize;
    ibegin_pMatrix += isize_pMatrix;
    ibegin_biovar += isize_biovar;
    ibegin_tlag += isize_tlag;
    isize = len[id];
    isize_pMatrix = len_pMatrix[id];
    isize_biovar = len_biovar[id];
    isize_tlag = len_tlag[id];

    std::vector<double> time_i(time_m.begin() + ibegin, time_m.begin() + ibegin + isize);
    std::vector<double> amt_i(amt_m.begin() + ibegin, amt_m.begin() + ibegin + isize);
    std::vector<double> rate_i(rate_m.begin() + ibegin, rate_m.begin() + ibegin + isize);
    std::vector<double> ii_i(ii_m.begin() + ibegin, ii_m.begin() + ibegin + isize);
    std::vector<int> evid_i(evid_m.begin() + ibegin, evid_m.begin() + ibegin + isize);
    std::vector<int> cmt_i(cmt_m.begin() + ibegin, cmt_m.begin() + ibegin + isize);
    std::vector<int> addl_i(addl_m.begin() + ibegin, addl_m.begin() + ibegin + isize);
    std::vector<int> ss_i(ss_m.begin() + ibegin, ss_m.begin() + ibegin + isize);
    std::vector<std::vector<double> > pMatrix_i(pMatrix_m.begin() + ibegin_pMatrix, pMatrix_m.begin() + ibegin_pMatrix + isize_pMatrix);
    std::vector<std::vector<double> > biovar_i(biovar_m.begin() + ibegin_biovar, biovar_m.begin() + ibegin_biovar + isize_biovar);
    std::vector<std::vector<double> > tlag_i(tlag_m.begin() + ibegin_tlag, tlag_m.begin() + ibegin_tlag + isize_tlag);

    ER events_rec(nCmt, time_i, amt_i, rate_i, ii_i, evid_i, cmt_i, addl_i, ss_i, pMatrix_i, biovar_i, tlag_i);
    EM em(events_rec);
    EM em_i(id, all_events_rec, ibegin_pMatrix, isize_pMatrix, ibegin_biovar, isize_biovar, ibegin_tlag, isize_tlag);
    EXPECT_EQ(em.events().num_state_times(), em_i.events().num_state_times());
    for (size_t j = 0; j < em.events().num_state_times(); ++j) {
      EXPECT_FLOAT_EQ(em.events().time(j), em_i.events().time(j));
      EXPECT_FLOAT_EQ(em.events().fractioned_rates(j)[1], em_i.events().fractioned_rates(j)[1]);
      EXPECT_EQ(em.events().evid(j), em_i.events().evid(j));
    }
  }
}


#include <boost/qvm/quat.hpp>


/*
 Copyright (C) 2019 Quaternion Risk Management Ltd
 All rights reserved.

 This file is part of ORE, a free-software/open-source library
 for transparent pricing and risk analysis - http://opensourcerisk.org

 ORE is free software: you can redistribute it and/or modify it
 under the terms of the Modified BSD License.  You should have received a
 copy of the license along with this program.
 The license is also available online at <http://opensourcerisk.org>

 This program is distributed on the basis that it will form a useful
 contribution to risk analytics and model standardisation, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
*/

/*! \file qle/indexes/equityindex.cpp
    \brief equity index class for holding equity fixing histories and forwarding.
    \ingroup indexes
*/

#include <qle/indexes/commodityindex.hpp>
#include <boost/make_shared.hpp>
#include <string>

using std::string;
using QuantExt::PriceTermStructure;

namespace QuantExt {

CommodityIndex::CommodityIndex(const std::string& underlyingName, const Date& expiryDate,
    const Calendar& fixingCalendar, const Handle<QuantExt::PriceTermStructure>& curve)
    : underlyingName_(underlyingName), expiryDate_(expiryDate), fixingCalendar_(fixingCalendar),
      curve_(curve), keepDays_(false) {
    init();
}

CommodityIndex::CommodityIndex(const string& underlyingName, const Date& expiryDate,
    const Calendar& fixingCalendar, bool keepDays, const Handle<PriceTermStructure>& curve)
    : underlyingName_(underlyingName), expiryDate_(expiryDate), fixingCalendar_(fixingCalendar),
      curve_(curve), keepDays_(keepDays) {
    init();
}

void CommodityIndex::init() {

    if (expiryDate_ == Date()) {
        // spot price index
        name_ = "COMM-" + underlyingName_;
        isFuturesIndex_ = false;
    } else {
        // futures price index
        std::ostringstream o;
        o << "COMM-" << underlyingName_ << "-" << QuantLib::io::iso_date(expiryDate_);
        name_ = o.str();
        if (!keepDays_) {
            // Remove the "-dd" portion from the expiry date
            name_.erase(name_.length() - 3);
        }
        isFuturesIndex_ = true;
    }

    registerWith(curve_);
    registerWith(Settings::instance().evaluationDate());
    registerWith(IndexManager::instance().notifier(name()));

}

Real CommodityIndex::fixing(const Date& fixingDate, bool forecastTodaysFixing) const {

    QL_REQUIRE(isValidFixingDate(fixingDate), "Commodity index " << name() << ": fixing date " <<
        io::iso_date(fixingDate) << " is not valid");
    QL_REQUIRE(expiryDate_ == Date() || fixingDate <= expiryDate_, "Commodity index " << name() <<
        ": fixing requested on fixing date (" << io::iso_date(fixingDate) << ") that is past the expiry date (" <<
        io::iso_date(expiryDate_) << ").");

    Date today = Settings::instance().evaluationDate();

    if (fixingDate > today || (fixingDate == today && forecastTodaysFixing))
        return forecastFixing(fixingDate);

    Real result = Null<Decimal>();

    if (fixingDate < today || Settings::instance().enforcesTodaysHistoricFixings()) {
        // must have been fixed
        // do not catch exceptions
        result = pastFixing(fixingDate);
        QL_REQUIRE(result != Null<Real>(), "Missing " << name() << " fixing for " << fixingDate);
    } else {
        try {
            // might have been fixed
            result = pastFixing(fixingDate);
        } catch (Error&) {
            ; // fall through and forecast
        }
        if (result == Null<Real>())
            return forecastFixing(fixingDate);
    }

    return result;
}

Real CommodityIndex::pastFixing(const Date& fixingDate) const {
    QL_REQUIRE(isValidFixingDate(fixingDate), fixingDate << " is not a valid fixing date");
    return timeSeries()[fixingDate];
}

Real CommodityIndex::forecastFixing(const Date& fixingDate) const {
    if (isFuturesIndex_)
        return curve_->price(expiryDate_);
    else
        return curve_->price(fixingDate);
}

boost::shared_ptr<CommodityIndex> CommoditySpotIndex::clone(const Date& expiryDate,
    const boost::optional<Handle<PriceTermStructure>>& ts) const {
    const auto& pts = ts ? *ts : priceCurve();
    return boost::make_shared<CommoditySpotIndex>(underlyingName(), fixingCalendar(), pts);
}

boost::shared_ptr<CommodityIndex> CommodityFuturesIndex::clone(const Date& expiry,
    const boost::optional<Handle<PriceTermStructure>>& ts) const {
    const auto& pts = ts ? *ts : priceCurve();
    const auto& ed = expiry == Date() ? expiryDate() : expiry;
    return boost::make_shared<CommodityFuturesIndex>(underlyingName(), ed, fixingCalendar(), keepDays(), pts);
}

} // namespace QuantExt


#pragma once

#include "defs.hh"
#include <vector>
#include <unordered_map>
#include <boost/functional/hash.hpp>

// Algorithm parameters
using Rankings = std::vector<Score>;
using OrigRankings = std::unordered_map<std::pair<ID,ID>, Score, boost::hash<std::pair<ID, ID> > >;

struct Params {
  Rankings rankings;
  Rankings rankingsOrigScores;
  std::string resultsDir;
  s32 nPeople, nAbstracts;
  s32 nTimeslots, nRooms, roomSize;
  s32 seed;
  u64 maxIterations;
  double initTemp, finalTemp;
  std::string personIdCol, abstractIdCol, scoreCol;
  char inputDelimiter;
  Score defaultScore;
  Score maxScore;
  Score minScore;
  Score scoreDelta;
  u32 participationRange;
  u32 avgParticipations;
  u32 minParticipations;
  u32 maxParticipations;
  u32 maxPresentations;
  Score maxNormScore; // = maxScore + scoreDelta
  Score minNormScore; // = minScore + scoreDelta)

  OrigRankings origRankings;
  std::vector<ID> personIdToOrig, abstractIdToOrig;
  std::unordered_map<ID, ID> personOrigIdToId, abstractOrigIdToId;
  std::set<ID> unrankedPersonIDs, unrankedAbstractIDs;
};

void outputParams(const Params& params, std::ostream& outStream);
bool readRankings(const std::string& filepath, Params& params);

inline bool validID(ID id) { return id != INVALID_ID; }
inline bool invalidID(ID id) { return !validID(id); }

inline Score getRanking(ID personID, ID abstractID, const Rankings& ranking, int nPeople) {
  return ranking.at((abstractID * nPeople) + personID);
}
inline Score getRanking(ID personID, ID abstractID, const Params& params) {
  return getRanking(personID, abstractID, params.rankings, params.nPeople);
}
inline Score getRankingOrig(ID personID, ID abstractID, const Params& params) {
  return getRanking(personID, abstractID, params.rankingsOrigScores, params.nPeople);
}
inline void setRanking(ID personID, ID abstractID, Rankings& ranking, int nPeople, Score score) {
  ranking[(abstractID * nPeople) + personID] = score;
}
inline void setRanking(ID personID, ID abstractID, Params& params, Score score) {
  setRanking(personID, abstractID, params.rankings, params.nPeople, score);
}


//Copyright (c) 2008-2017 Emil Dotchevski and Reverge Studios, Inc.

//Distributed under the Boost Software License, Version 1.0. (See accompanying
//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_QVM_AD4F14F2444066D06BC430B7323BA122
#define BOOST_QVM_AD4F14F2444066D06BC430B7323BA122

//This file was generated by a program. Do not edit manually.

#include <boost/qvm/enable_if.hpp>
#include <boost/qvm/inline.hpp>
#include <boost/qvm/mat_traits.hpp>

namespace
boost
    {
    namespace
    qvm
        {
        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename enable_if_c<
            mat_traits<A>::rows==4 && mat_traits<B>::rows==4 &&
            mat_traits<A>::cols==4 && mat_traits<B>::cols==4,
            A &>::type
        assign( A & a, B const & b )
            {
            mat_traits<A>::template write_element<0,0>(a)=mat_traits<B>::template read_element<0,0>(b);
            mat_traits<A>::template write_element<0,1>(a)=mat_traits<B>::template read_element<0,1>(b);
            mat_traits<A>::template write_element<0,2>(a)=mat_traits<B>::template read_element<0,2>(b);
            mat_traits<A>::template write_element<0,3>(a)=mat_traits<B>::template read_element<0,3>(b);
            mat_traits<A>::template write_element<1,0>(a)=mat_traits<B>::template read_element<1,0>(b);
            mat_traits<A>::template write_element<1,1>(a)=mat_traits<B>::template read_element<1,1>(b);
            mat_traits<A>::template write_element<1,2>(a)=mat_traits<B>::template read_element<1,2>(b);
            mat_traits<A>::template write_element<1,3>(a)=mat_traits<B>::template read_element<1,3>(b);
            mat_traits<A>::template write_element<2,0>(a)=mat_traits<B>::template read_element<2,0>(b);
            mat_traits<A>::template write_element<2,1>(a)=mat_traits<B>::template read_element<2,1>(b);
            mat_traits<A>::template write_element<2,2>(a)=mat_traits<B>::template read_element<2,2>(b);
            mat_traits<A>::template write_element<2,3>(a)=mat_traits<B>::template read_element<2,3>(b);
            mat_traits<A>::template write_element<3,0>(a)=mat_traits<B>::template read_element<3,0>(b);
            mat_traits<A>::template write_element<3,1>(a)=mat_traits<B>::template read_element<3,1>(b);
            mat_traits<A>::template write_element<3,2>(a)=mat_traits<B>::template read_element<3,2>(b);
            mat_traits<A>::template write_element<3,3>(a)=mat_traits<B>::template read_element<3,3>(b);
            return a;
            }

        namespace
        sfinae
            {
            using ::boost::qvm::assign;
            }

        namespace
        qvm_detail
            {
            template <int R,int C>
            struct assign_mm_defined;

            template <>
            struct
            assign_mm_defined<4,4>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename enable_if_c<
            mat_traits<A>::rows==4 && mat_traits<B>::rows==4 &&
            mat_traits<A>::cols==1 && mat_traits<B>::cols==1,
            A &>::type
        assign( A & a, B const & b )
            {
            mat_traits<A>::template write_element<0,0>(a)=mat_traits<B>::template read_element<0,0>(b);
            mat_traits<A>::template write_element<1,0>(a)=mat_traits<B>::template read_element<1,0>(b);
            mat_traits<A>::template write_element<2,0>(a)=mat_traits<B>::template read_element<2,0>(b);
            mat_traits<A>::template write_element<3,0>(a)=mat_traits<B>::template read_element<3,0>(b);
            return a;
            }

        namespace
        sfinae
            {
            using ::boost::qvm::assign;
            }

        namespace
        qvm_detail
            {
            template <int R,int C>
            struct assign_mm_defined;

            template <>
            struct
            assign_mm_defined<4,1>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename enable_if_c<
            mat_traits<A>::rows==1 && mat_traits<B>::rows==1 &&
            mat_traits<A>::cols==4 && mat_traits<B>::cols==4,
            A &>::type
        assign( A & a, B const & b )
            {
            mat_traits<A>::template write_element<0,0>(a)=mat_traits<B>::template read_element<0,0>(b);
            mat_traits<A>::template write_element<0,1>(a)=mat_traits<B>::template read_element<0,1>(b);
            mat_traits<A>::template write_element<0,2>(a)=mat_traits<B>::template read_element<0,2>(b);
            mat_traits<A>::template write_element<0,3>(a)=mat_traits<B>::template read_element<0,3>(b);
            return a;
            }

        namespace
        sfinae
            {
            using ::boost::qvm::assign;
            }

        namespace
        qvm_detail
            {
            template <int R,int C>
            struct assign_mm_defined;

            template <>
            struct
            assign_mm_defined<1,4>
                {
                static bool const value=true;
                };
            }

        }
    }

#endif


#include "wali/graph/Functional.hpp"
#include "wali/graph/IntraGraph.hpp"

#include "llvm/Support/ErrorHandling.h"

#include <boost/cast.hpp>

using namespace wali;
using namespace std;
using namespace wali::graph;

SemElemFunctional::SemElemFunctional() :
  type(TRASH),
  lhs(NULL),
  rhs(NULL),
  value(NULL),
  intra_nodeno(-1)
{}

SemElemFunctional::SemElemFunctional(sem_elem_tensor_t wt) :
  type(Constant),
  lhs(NULL),
  rhs(NULL),
  value(wt),
  intra_nodeno(-1)
{}

SemElemFunctional::SemElemFunctional(int n) :
  type(In),
  lhs(NULL),
  rhs(NULL),
  value(NULL),
  intra_nodeno(n)
{}

functional_t SemElemFunctional::constant(sem_elem_tensor_t wt)
{
  return new SemElemFunctional(wt);
}

functional_t SemElemFunctional::in(int n)
{
  return new SemElemFunctional(n);
}

functional_t SemElemFunctional::extend(functional_t lhs, functional_t rhs)
{
  functional_t res = new SemElemFunctional();
  res->type = Extend;
  res->lhs = lhs;
  res->rhs = rhs;
  return res;
}

functional_t SemElemFunctional::combine(functional_t lhs, functional_t rhs)
{
  functional_t res = new SemElemFunctional();
  res->type = Combine;
  res->lhs = lhs;
  res->rhs = rhs;
  return res;
}

functional_t SemElemFunctional::tensor(functional_t lhs, functional_t rhs)
{
  functional_t res = new SemElemFunctional();
  res->type = Tensor;
  res->lhs = lhs;
  res->rhs = rhs;
  return res;
}

functional_t SemElemFunctional::detensor(functional_t arg)
{
  functional_t res = new SemElemFunctional();
  res->type = Detensor;
  res->lhs = arg;
  return res;
}

functional_t SemElemFunctional::detensorTranspose(functional_t arg)
{
  functional_t res = new SemElemFunctional();
  res->type = DetensorTranspose;
  res->lhs = arg;
  return res;
}

functional_t SemElemFunctional::transpose(functional_t arg)
{
  functional_t res = new SemElemFunctional();
  res->type = Transpose;
  res->lhs = arg;
  return res;
}

sem_elem_tensor_t SemElemFunctional::evaluate(IntraGraph * const gr)
{
  sem_elem_tensor_t lval, rval, val;
  switch(type){
    case Constant:
      return value;
    case In:
      return boost::polymorphic_downcast<SemElemTensor*>(gr->getWeight(intra_nodeno).get_ptr());
    case Extend:
       lval = lhs->evaluate(gr);
       rval = rhs->evaluate(gr);
      return boost::polymorphic_downcast<SemElemTensor*>(lval->extend(rval.get_ptr()).get_ptr());
    case Combine:
       lval = lhs->evaluate(gr);
       rval = rhs->evaluate(gr);
      return boost::polymorphic_downcast<SemElemTensor*>(lval->combine(rval.get_ptr()).get_ptr());
    case Tensor:
       lval = lhs->evaluate(gr);
       rval = rhs->evaluate(gr);
      return boost::polymorphic_downcast<SemElemTensor*>(lval->tensor(rval.get_ptr()).get_ptr());
    case Detensor:
       val = lhs->evaluate(gr);
      return boost::polymorphic_downcast<SemElemTensor*>(val->detensor().get_ptr());
    case DetensorTranspose:
       val = lhs->evaluate(gr);
      return boost::polymorphic_downcast<SemElemTensor*>(val->detensorTranspose().get_ptr());
    case Transpose:
       val = lhs->evaluate(gr);
       return boost::polymorphic_downcast<SemElemTensor*>(val->transpose().get_ptr());
    default:
      llvm_unreachable("[SemElemFunctiona::evaluate] Unknown case\n");
  }
}

void SemElemFunctional::leafNodes(std::vector<int>& leaves)
{
  std::vector<int> ret;
  switch(type){
    case Constant:
      break;
    case In:
      leaves.push_back(intra_nodeno);
      break;
    case Extend:
    case Combine:
    case Tensor:
       lhs->leafNodes(leaves);
       rhs->leafNodes(leaves);
       break;
    case Detensor:
    case DetensorTranspose:
    case Transpose:
       lhs->leafNodes(leaves);
       break;
    default:
      assert(false && "[SemElemFunctiona::evaluate] Unknown case\n");
  }
}


// Copyright (c) 2021 CNES
//
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#pragma once
#include <pybind11/numpy.h>

#include <Eigen/Core>
#include <optional>
#include <tuple>
#include <unordered_map>
#include <vector>

#include "pyinterp/eigen.hpp"
#include "pyinterp/geodetic/box.hpp"
#include "pyinterp/geodetic/point.hpp"

namespace pyinterp::geohash::string {

/// Handle the numpy arrays of geohash.
class Array {
 public:
  /// Creation of a vector of "size" items of strings of maximum length
  /// "precision"
  Array(const size_t size, const uint32_t precision)
      : array_(new std::vector<char>(size * precision, '\0')),
        capsule_(
            array_,
            [](void* ptr) { delete static_cast<std::vector<char>*>(ptr); }),
        chars_(precision),
        size_(size) {}

  /// Get the pointer to the raw memory
  [[nodiscard]] inline auto buffer() const -> char* { return array_->data(); }

  /// Creates the numpy array from the memory allocated in the C++ code without
  /// copying the data.
  [[nodiscard]] inline auto pyarray() -> pybind11::array {
    return pybind11::array(pybind11::dtype("S" + std::to_string(chars_)),
                           {size_}, {chars_ * sizeof(char)}, array_->data(),
                           capsule_);
  }

  static auto get_info(const pybind11::array& hash, ssize_t ndim)
      -> pybind11::buffer_info;

 private:
  std::vector<char>* array_;
  pybind11::capsule capsule_;
  uint32_t chars_;
  size_t size_;
};

/// Encode a point into geohash with the given bit depth
auto encode(const geodetic::Point& point, char* buffer, uint32_t precision)
    -> void;

/// Encode points into geohash with the given bit depth
[[nodiscard]] auto encode(const Eigen::Ref<const Eigen::VectorXd>& lon,
                          const Eigen::Ref<const Eigen::VectorXd>& lat,
                          uint32_t precision) -> pybind11::array;

/// Returns the region encoded
[[nodiscard]] auto bounding_box(const char* hash, size_t count)
    -> geodetic::Box;

/// Decode a hash into a spherical equatorial point. If round is true, the
/// coordinates of the points will be rounded to the accuracy defined by the
/// GeoHash.
[[nodiscard]] auto decode(const char* hash, size_t count, bool round)
    -> geodetic::Point;

/// Decode hashes into a spherical equatorial points. If round is true, the
/// coordinates of the points will be rounded to the accuracy defined by the
/// GeoHash.
[[nodiscard]] auto decode(const pybind11::array& hash, bool round)
    -> std::tuple<Eigen::VectorXd, Eigen::VectorXd>;

/// Returns all neighbors hash clockwise from north around northwest at the
/// given precision:
///   7 0 1
///   6 x 2
///   5 4 3
[[nodiscard]] auto neighbors(const char* hash, size_t count) -> pybind11::array;

/// Returns the area covered by the GeoHash
[[nodiscard]] inline auto area(const char* const hash, size_t count,
                               const std::optional<geodetic::System>& wgs)
    -> double {
  return bounding_box(hash, count).area(wgs);
}

/// Returns the area covered by the GeoHash codes
[[nodiscard]] auto area(const pybind11::array& hash,
                        const std::optional<geodetic::System>& wgs)
    -> Eigen::MatrixXd;

/// Returns all GeoHash with the defined box
[[nodiscard]] auto bounding_boxes(const std::optional<geodetic::Box>& box,
                                  uint32_t precision) -> pybind11::array;

/// Returns the start and end indexes of the different GeoHash boxes.
[[nodiscard]] auto where(const pybind11::array& hash) -> std::unordered_map<
    std::string,
    std::tuple<std::tuple<int64_t, int64_t>, std::tuple<int64_t, int64_t>>>;

}  // namespace pyinterp::geohash::string


/*
@copyright Louis Dionne 2014
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#include <boost/hana/ext/std/tuple.hpp>

#include <boost/hana/detail/assert.hpp>
#include <boost/hana/detail/constexpr.hpp>
#include <boost/hana/integral.hpp>

#include <tuple>
using namespace boost::hana;

BOOST_HANA_CONSTEXPR_LAMBDA auto f = [](auto i) { return i + int_<1>; };

int main() {
    BOOST_HANA_CONSTEXPR_ASSERT(fmap(f, std::make_tuple()) == std::make_tuple());
    BOOST_HANA_CONSTEXPR_ASSERT(fmap(f, std::make_tuple(int_<1>)) == std::make_tuple(f(int_<1>)));
    BOOST_HANA_CONSTEXPR_ASSERT(fmap(f, std::make_tuple(int_<1>, int_<2>)) == std::make_tuple(f(int_<1>), f(int_<2>)));
    BOOST_HANA_CONSTEXPR_ASSERT(fmap(f, std::make_tuple(int_<1>, int_<2>, int_<3>)) == std::make_tuple(f(int_<1>), f(int_<2>), f(int_<3>)));
}


#include <boost/numeric/odeint/iterator/adaptive_time_iterator.hpp>


#include <boost/hana/is_disjoint.hpp>


#include <Eigen/Core>

#include <numpy_eigen/boost_python_headers.hpp>
Eigen::Matrix<boost::int64_t, 5, 3> test_long_5_3(const Eigen::Matrix<boost::int64_t, 5, 3>& M) { return M; }
void export_long_5_3() { boost::python::def("test_long_5_3", test_long_5_3); }


// src
// Luc Oth 04/12
// othlu@student.ethz.ch

#include <aslam/cameras/PinholeRSCameraGeometry.hpp>
#include <aslam/Exceptions.hpp>
#include <Eigen/Dense>

namespace aslam {
namespace cameras {

PinholeRSCameraGeometry::PinholeRSCameraGeometry() {
  *this = createTestGeometry();
}

PinholeRSCameraGeometry::PinholeRSCameraGeometry(double focalLengthU,
                                                 double focalLengthV,
                                                 double imageCenterU,
                                                 double imageCenterV,
                                                 int resolutionU,
                                                 int resolutionV,
                                                 double lineDelay)
    : _fu(focalLengthU),
      _fv(focalLengthV),
      _cu(imageCenterU),
      _cv(imageCenterV),
      _ru(resolutionU),
      _rv(resolutionV),
      _enableDistortion(0),
      _recip_fu(1.0 / _fu),
      _recip_fv(1.0 / _fv),
      _fu_over_fv(_fu / _fv),
      _lineDelay(lineDelay) {
  // 0
}

PinholeRSCameraGeometry::PinholeRSCameraGeometry(double focalLengthU,
                                                 double focalLengthV,
                                                 double imageCenterU,
                                                 double imageCenterV,
                                                 int resolutionU,
                                                 int resolutionV,
                                                 double lineDelay, double k1,
                                                 double k2, double p1,
                                                 double p2)
    : _fu(focalLengthU),
      _fv(focalLengthV),
      _cu(imageCenterU),
      _cv(imageCenterV),
      _ru(resolutionU),
      _rv(resolutionV),
      _k1(k1),
      _k2(k2),
      _p1(p1),
      _p2(p2),
      _enableDistortion(1),
      _recip_fu(1.0 / _fu),
      _recip_fv(1.0 / _fv),
      _fu_over_fv(_fu / _fv),
      _lineDelay(lineDelay) {
  // 0
}

PinholeRSCameraGeometry::~PinholeRSCameraGeometry() {

}

// This updates the intrinsic parameters with a small step: i <-- i + di
// The Jacobians above are with respect to this update function.
void PinholeRSCameraGeometry::updateIntrinsicsOplus(double * di) {
  SM_THROW(NotImplementedException,
           "The pinhole camera object does not support optimizing intrinsics");
}

// The amount of time elapsed between the start of the image and the
// keypoint. For a global shutter camera, this can return Duration(0).
Duration PinholeRSCameraGeometry::temporalOffset(
    const keypoint_t & keypoint) const {

  // time offset only depends on the line => keypoint(1)
  return Duration(_lineDelay * keypoint(1));

}

PinholeRSCameraGeometry::keypoint_t PinholeRSCameraGeometry::maxKeypoint() const {
  return keypoint_t(_ru, _rv);
}

PinholeRSCameraGeometry::keypoint_t PinholeRSCameraGeometry::minKeypoint() const {
  return keypoint_t(0, 0);
}

std::string PinholeRSCameraGeometry::typeName() const {
  return "PinholeRSCamera";
}

PinholeRSCameraGeometry PinholeRSCameraGeometry::createTestGeometry() {
  return PinholeRSCameraGeometry(400, 400, 320, 240, 640, 480, 0.0005);
}

PinholeRSCameraGeometry PinholeRSCameraGeometry::createDistortedTestGeometry() {
  return PinholeRSCameraGeometry(400, 400, 320, 240, 640, 480, 0.0005, 0.2, 0.2,
                                 0.2, 0.2);
}

/**
 * \brief Apply distortion to input point (from the normalised plane)
 *
 * \param mx_u undistorted x coordinate of point on the normalised plane
 * \param my_u undistorted y coordinate of point on the normalised plane
 * \param dx return value, to obtain the distorted point : mx_d = mx_u+dx_u
 * \param dy return value, to obtain the distorted point : my_d = my_u+dy_u
 */
void PinholeRSCameraGeometry::distortion(double mx_u, double my_u, double *dx_u,
                                         double *dy_u) const {
  double mx2_u, my2_u, mxy_u, rho2_u, rad_dist_u;

  mx2_u = mx_u * mx_u;
  my2_u = my_u * my_u;
  mxy_u = mx_u * my_u;
  rho2_u = mx2_u + my2_u;
  rad_dist_u = _k1 * rho2_u + _k2 * rho2_u * rho2_u;
  *dx_u = mx_u * rad_dist_u + 2 * _p1 * mxy_u + _p2 * (rho2_u + 2 * mx2_u);
  *dy_u = my_u * rad_dist_u + 2 * _p2 * mxy_u + _p1 * (rho2_u + 2 * my2_u);
}

/**
 * \brief Apply distortion to input point (from the normalised plane)
 *        and calculate jacobian
 *
 * \param mx_u undistorted x coordinate of point on the normalised plane
 * \param my_u undistorted y coordinate of point on the normalised plane
 * \param dx return value, to obtain the distorted point : mx_d = mx_u+dx_u
 * \param dy return value, to obtain the distorted point : my_d = my_u+dy_u
 */
void PinholeRSCameraGeometry::distortion(double mx_u, double my_u, double *dx_u,
                                         double *dy_u, double *dxdmx,
                                         double *dydmx, double *dxdmy,
                                         double *dydmy) const {
  double mx2_u, my2_u, mxy_u, rho2_u, rad_dist_u;

  mx2_u = mx_u * mx_u;
  my2_u = my_u * my_u;
  mxy_u = mx_u * my_u;
  rho2_u = mx2_u + my2_u;
  rad_dist_u = _k1 * rho2_u + _k2 * rho2_u * rho2_u;
  *dx_u = mx_u * rad_dist_u + 2 * _p1 * mxy_u + _p2 * (rho2_u + 2 * mx2_u);
  *dy_u = my_u * rad_dist_u + 2 * _p2 * mxy_u + _p1 * (rho2_u + 2 * my2_u);

  *dxdmx = 1 + rad_dist_u + _k1 * 2 * mx2_u + _k2 * rho2_u * 4 * mx2_u
      + 2 * _p1 * my_u + 6 * _p2 * mx_u;
  *dydmx = _k1 * 2 * mx_u * my_u + _k2 * 4 * rho2_u * mx_u * my_u
      + _p1 * 2 * mx_u + 2 * _p2 * my_u;
  *dxdmy = *dydmx;
  *dydmy = 1 + rad_dist_u + _k1 * 2 * my2_u + _k2 * rho2_u * 4 * my2_u
      + 6 * _p1 * my_u + 2 * _p2 * mx_u;
}

// Use Gauss-Newton to undistort.
void PinholeRSCameraGeometry::undistortGN(double u_d, double v_d, double * u,
                                          double * v) const {
  *u = u_d;
  *v = v_d;

  double ubar = u_d;
  double vbar = v_d;
  const int n = 5;
  Eigen::Matrix2d F;

  double hat_u_d;
  double hat_v_d;

  for (int i = 0; i < n; i++) {
    distortion(ubar, vbar, &hat_u_d, &hat_v_d, &F(0, 0), &F(1, 0), &F(0, 1),
               &F(1, 1));

    Eigen::Vector2d e(u_d - ubar - hat_u_d, v_d - vbar - hat_v_d);
    Eigen::Vector2d du = (F.transpose() * F).inverse() * F.transpose() * e;

    ubar += du[0];
    vbar += du[1];

    if (e.dot(e) < 1e-15)
      break;

  }
  *u = ubar;
  *v = vbar;
}

void PinholeRSCameraGeometry::setIntrinsicsVectorImplementation(
    const PinholeRSCameraGeometry::traits_t::intrinsics_t & intrinsics) {
  _fu = intrinsics(0);
  _fv = intrinsics(1);
  _cu = intrinsics(2);
  _cv = intrinsics(3);
  _k1 = intrinsics(4);
  _k2 = intrinsics(5);
  _p1 = intrinsics(6);
  _p2 = intrinsics(7);
  _recip_fu = 1.0 / _fu;
  _recip_fv = 1.0 / _fv;
  _fu_over_fv = _fu / _fv;

}

PinholeRSCameraGeometry::traits_t::intrinsics_t PinholeRSCameraGeometry::getIntrinsicsVectorImplementation() const {
  traits_t::intrinsics_t intrinsics;
  intrinsics(0) = _fu;
  intrinsics(1) = _fv;
  intrinsics(2) = _cu;
  intrinsics(3) = _cv;
  intrinsics(4) = _k1;
  intrinsics(5) = _k2;
  intrinsics(6) = _p1;
  intrinsics(7) = _p2;
  return intrinsics;
}

}  // namespace cameras

}  // namespace aslam


#ifndef CAFFE_SMILE_NET_H_
#define CAFFE_SMILE_NET_H_

#include "caffe/caffe.hpp"
#include "caffe/proto/caffe.pb.h"
#include <boost/shared_ptr.hpp>

#include "caffe/tracker/bounding_box.hpp"

#include <opencv2/core/core.hpp>
#include <opencv2/opencv.hpp>

namespace caffe {

class SmileNetWrapper {
public:
  SmileNetWrapper(const std::string& proto, const std::string& model);

  bool is_smile(const cv::Mat& image, const BoundingBox<float>& roi, float* score);
  void is_smile(const cv::Mat& image, const vector<BoundingBox<float> >& rois, vector<bool>* smile, vector<float>* scores);

private:
  void getCropPatch(const cv::Mat& image, const BoundingBox<float>& roi, cv::Mat* patch);
  void load(const cv::Mat& image);
  void load(const vector<cv::Mat>& images);

  boost::shared_ptr<caffe::Net<float> > net_;
};

}

#endif


#include "iogroup-hwpwm.hpp"
#include <boost/algorithm/string.hpp>
#include <algorithm>
#include <cmath>
#include <vector>

using namespace std;
using namespace libconfig;

IoGroupHwPwm::PwmPin::PwmPin(std::string handle, IoGroupHwPwm *iogroup, libconfig::Setting &setting)
{
	int32_t inttemp;
	double doubletemp;

	this->gamma = 2.8;
	this->maxValue = 1.0;
	this->minValue = 0.0;
	this->value = 0.0;
	this->offset = 0.0;
	this->servoMinTimeMs = 1.0;
	this->servoMaxTimeMs = 2.0;
	this->valueInverse = false;
	this->servoInverse = false;

	this->iogroup = iogroup;
	this->handle = handle;

	clog << "Initializing PWM pin '"<< handle << "'" << endl;

	// default values
	this->filter = PwmFilterNone;
	this->id = -1;

	// parse
	this->id = this->iogroup->getPinId(setting);

	string pwmfilter = "none";
	setting.lookupValue("filter",pwmfilter);
	setting.lookupValue("gamma",this->gamma);

	if(setting.lookupValue("max",doubletemp))
		this->maxValue = doubletemp;
	else if(setting.lookupValue("max",inttemp))
		this->maxValue = (double)inttemp;
	if(setting.lookupValue("min",doubletemp))
		this->minValue = doubletemp;
	else if(setting.lookupValue("min",inttemp))
		this->minValue = (double)inttemp;
	if(setting.lookupValue("default",doubletemp))
		this->value = doubletemp;
	else if(setting.lookupValue("default",inttemp))
		this->value = (double)inttemp;
	if(setting.lookupValue("servo-min-ms",doubletemp))
		this->servoMinTimeMs = doubletemp;
	else if(setting.lookupValue("servo-min-ms",inttemp))
		this->servoMinTimeMs = (double)inttemp;
	if(setting.lookupValue("servo-max-ms",doubletemp))
		this->servoMaxTimeMs = doubletemp;
	else if(setting.lookupValue("servo-max-ms",inttemp))
		this->servoMaxTimeMs = (double)inttemp;
	if(setting.lookupValue("offset",doubletemp))
		this->offset = doubletemp;
	else if(setting.lookupValue("offset",inttemp))
		this->offset = (double)inttemp;

	if(this->offset < 0 || this->offset >= 1)
	{
		this->offset = 0;
	}

	if(this->servoMinTimeMs > this->servoMaxTimeMs)
	{
		double d = this->servoMinTimeMs;
		this->servoMinTimeMs = this->servoMaxTimeMs;
		this->servoMaxTimeMs = d;
		this->servoInverse = true;
	}

	if(this->minValue > this->maxValue)
	{
		double d = this->minValue;
		this->minValue = this->maxValue;
		this->maxValue = d;
		this->valueInverse = true;
	}

	if(this->value < this->minValue)
	{
		this->value = this->minValue;
	}
	else if(this->value > this->maxValue)
	{
		this->value = this->maxValue;
	}

	if(boost::iequals(pwmfilter,"led"))
	{
		this->filter = PwmPin::PwmFilterLed;
	}
	else if(boost::iequals(pwmfilter,"servo"))
	{
		this->filter = PwmPin::PwmFilterServo;
		this->gamma = 1;
	}
	else
	{
		this->filter = PwmPin::PwmFilterNone;
	}

	clog << "PWM pin config: " << endl;
	clog << kLogInfo << "id                : " << (int32_t)this->id << endl;
	clog << kLogInfo << "handle            : " << this->handle << endl;
	clog << kLogInfo << "filter            : " ;
	if(this->filter == PwmPin::PwmFilterServo)
		clog << "SERVO" << endl;
	else if(this->filter == PwmPin::PwmFilterLed)
		clog << "LED" << endl;
	else
		clog << "NONE" << endl;

	clog << kLogInfo << "invert value      : " << ((this->valueInverse)?"true":"false") << endl;
	clog << kLogInfo << "min               : " << this->minValue << endl;
	clog << kLogInfo << "max               : " << this->maxValue << endl;
	clog << kLogInfo << "default           : " << this->value << endl;
	clog << kLogInfo << "offset            : " << this->offset << endl;
	clog << kLogInfo << "gamma             : " << this->gamma << endl;
	clog << kLogInfo << "servo min         : " << this->servoMinTimeMs << "ms" << endl;
	clog << kLogInfo << "servo max         : " << this->servoMaxTimeMs << "ms" << endl;
	clog << kLogInfo << "servo invert      : " << ((this->servoInverse)?"true":"false") << endl;

}

double IoGroupHwPwm::PwmPin::GetFilteredValue()
{
	double fractionValue = (this->value - this->minValue) / (this->maxValue - this->minValue);
	double result = 0;

	double t_ms;
	double valServoMin;
	double valServoMax;

	// if the max and min were reversed in the configuration,
	clog << kLogDebug;
	clog << "    Set value           : " << this->value << endl;
	clog << "    Min value           : " << this->minValue << endl;
	clog << "    Max value           : " << this->maxValue << endl;

	clog << "    Input value swap    : ";
	if(this->valueInverse)
	{
		fractionValue = 1 - fractionValue;
		clog << "true" << endl;
	}
	else
	{
		clog << "false" << endl;
	}

	clog << "    Unfiltered fraction : " << fractionValue << endl;

	clog << "    Using filter        : " ;
	switch(this->filter)
	{
	case PwmPin::PwmFilterLed:
		clog << "LED" << endl;
		result = this->ForwardGamma(fractionValue);
		break;
	case PwmPin::PwmFilterServo:
		clog << "SERVO" << endl;
		// Determine min and max pulse fractions for the servo control
		t_ms = this->iogroup->GetPwmPeriodMs();
		valServoMin = this->servoMinTimeMs / t_ms;
		valServoMax = this->servoMaxTimeMs / t_ms;

		clog << "    PWM cycle duration  : " << t_ms << "ms" << endl;
		clog << "    Min pulse fraction  : " << valServoMin << "(" << this->servoMinTimeMs << "ms)" << endl;
		clog << "    Max pulse fraction  : " << valServoMax << "(" << this->servoMaxTimeMs << "ms)" << endl;

		if(valServoMax >= 1)
		{
			// if we cannot provide the full range, don't do anything at all.
			clog << kLogWarning << this->iogroup->Name() << "|" << this->handle << " : Cannot set servo values.";
			clog << "Maximum servo pulse of " << this->servoMaxTimeMs << "ms exceeds pwm period time of " << t_ms << "ms." << endl;
			clog << kLogDebug;
			result = 0;
		}
		else
		{
			// if the servo max and min were reversed in the configuration, inverse value again
			clog << "    Servo directon swap : ";
			if(this->servoInverse)
			{
				fractionValue = 1 - fractionValue;
				clog << "true" << endl;
			}
			else
			{
				clog << "false" << endl;
			}
			result = valServoMin + fractionValue * (valServoMax - valServoMin);
		}
		break;
	default:
		clog << "NONE" << endl;
		result = fractionValue;
	}

	clog << "    Filtered fraction   : " << result << endl;

	return result;
}

void IoGroupHwPwm::PwmPin::SetFromFilteredValue(const double &filteredValue,const double &offset)
{
	double t_ms;
	double valServoMin;
	double valServoMax;

	double fractionValue = 0;
	switch(this->filter)
	{
	case PwmPin::PwmFilterLed:
		fractionValue = this->ReverseGamma(filteredValue);
		break;
	case PwmPin::PwmFilterServo:
		// Determine min and max pulse fractions for the servo control
		t_ms = this->iogroup->GetPwmPeriodMs();
		valServoMin = this->servoMinTimeMs / t_ms;
		valServoMax = this->servoMaxTimeMs / t_ms;

		if(valServoMax >= 1)
		{
			// if we cannot provide the full range, don't do anything at all.
			clog << kLogWarning << this->iogroup->Name() << "|" << this->handle << " : Cannot set servo values.";
			clog << "Maximum servo pulse of " << this->servoMaxTimeMs << "ms exceeds pwm period time of " << t_ms << "ms." << endl;
			fractionValue = 0;
		}
		else
		{
			fractionValue = (filteredValue - valServoMin) / (valServoMax - valServoMin);
			// if the max and min were reversed in the configuration,
			if(this->servoInverse)
			{
				fractionValue = 1 - fractionValue;
			}
		}
		break;
	default:
		fractionValue = filteredValue;
	}

	// if the max and min were reversed in the configuration,
	if(this->valueInverse)
	{
		fractionValue = 1 - fractionValue;
	}

	this->value = this->minValue + fractionValue * (this->maxValue - this->minValue);

	if(offset >= 0 && offset < 1)
	{
		this->offset = offset;
	}
}

double IoGroupHwPwm::PwmPin::ForwardGamma(const double &value)
{
	return pow(value,this->gamma);
}

double IoGroupHwPwm::PwmPin::ReverseGamma(const double &value)
{
	double invgamma = 0;
	if(this->gamma != 0)
	{
		invgamma = 1/this->gamma;
	}
	return pow(value/1,invgamma);
}

uint16_t IoGroupHwPwm::PwmPin::GetId()
{
	return this->id;
}

const std::string IoGroupHwPwm::PwmPin::GetHandle()
{
	return this->handle;
}

IoGroupHwPwm::PwmPin::PwmFilter IoGroupHwPwm::PwmPin::GetFilter()
{
	return this->filter;
}

double IoGroupHwPwm::PwmPin::GetGamma()
{
	return this->gamma;
}

double IoGroupHwPwm::PwmPin::GetValue()
{
	return this->value;
}

double IoGroupHwPwm::PwmPin::GetOffset()
{
	return this->offset;
}

void IoGroupHwPwm::PwmPin::SetValue(const double &value)
{
	if(value < this->minValue)
	{
		this->value = this->minValue;
	}
	else if (value > this->maxValue)
	{
		this->value = this->maxValue;
	}
	else
	{
		this->value = value;
	}
}

double IoGroupHwPwm::PwmPin::GetMin(){
	return this->minValue;
}

double IoGroupHwPwm::PwmPin::GetMax(){
	return this->maxValue;
}

// public

IoGroupHwPwm::IoGroupHwPwm(DBus::Connection &connection, std::string &dbuspath, GpioRegistry &registry)
    : IoGroupBase(connection, dbuspath, registry)//, DBus::ObjectAdaptor(connection, dbuspath)
{

}

IoGroupHwPwm::~IoGroupHwPwm()
{
	// delete all PwmPin objects
	std::set<PwmPin*>::iterator it;
	for( it = this->pwmPins.begin(); it != this->pwmPins.end(); ++ it)
	{
		delete *it;
	}
}

void IoGroupHwPwm::Initialize(libconfig::Setting &setting)
{
	clog << kLogDebug << "IoGroupHwPwm Initializing " << endl;

    IoGroupBase::Initialize(setting);
	this->pwmFrequency = 100;

    // Check if we have an IO, and if we have defined ios, otherwise just ignore everything here
    if(setting.exists("io"))
    {
        Setting& io_list = setting["io"];
		if(io_list.getLength() > 0)
		{

			// Read button timer settings from setting
			setting.lookupValue("pwm-frequency",this->pwmFrequency);

			clog << kLogDebug << "PWM frequency: " << this->pwmFrequency << endl;

			// Initialize button timer

			// Nofify subclass of start of configuration iteration
			this->beginConfig(setting);

			for(int i=0; i<io_list.getLength(); ++i)
			{
				Setting& io = io_list[i];
				string handle = io.getName();
				try
				{
					PwmPin * pin = new PwmPin(handle,this,io);

					if(this->registerPwmPin(pin)) //verify if handle can be registered (no double registrations for handle or id
					{
						this->preparePwmPin(pin);
						this->pwmPins.insert(pin);	// cached list of pins used for proper cleanup in destructor
						this->pwmList.insert(pin->GetHandle()); // cached list of handles
					}
			        else
			        {
			        	clog << kLogWarning << this->Name() << ": Could not register handle '" << handle << "' with id '" << pin->GetId() << "' as pwm output" << endl;
			        	delete pin;
			        }
				}
				catch(IoPinInvalidException &x)
				{
			        clog << kLogError << this->Name() << ": Invalid IO pin specified for handle '" << handle << "' :" << x.what() << endl;
				}
			    catch(FeatureNotImplementedException &x)
			    {
			        clog << kLogError << this->Name() << ": " << x.what() << endl;
			    }

			}
			// Nofify subclass of start of configuration iteration
			this->endConfig();
		}
    }

}

std::vector<std::string> IoGroupHwPwm::Pwms()
{
    std::vector<std::string> output(this->pwmList.begin(), this->pwmList.end());
    return output;
}

void IoGroupHwPwm::SetValue(const std::string &handle, const double &value)
{
    try
    {
        if(this->handleMap.count(handle) > 0)
        {
            clog << kLogDebug << this->Name() << ": Setting pwm value on handle '" << handle << "' to '" << value << "'" << endl;
            PwmPin * pin = this->handleMap[handle];
            pin->SetValue(value);
            this->setPwmPin(pin);

            this->PwmValueChanged(handle,value);
        }
        else
        {
            clog << kLogWarning << this->Name() << ": Attempt to call SetValue for nonexisting handle '" << handle << "'" << endl;
        }
    }
    catch(FeatureNotImplementedException &x)
    {
        clog << kLogError << this->Name() << ": PWM Not implemented, but SetValue called nonetheless'" << handle << "'" << endl;
    }
}

double IoGroupHwPwm::GetValue(const std::string &handle)
{
    try
    {
		if(this->handleMap.count(handle) > 0)
		{
			clog << kLogDebug << this->Name() << ": Getting pwm value on handle '" << handle << "'" << endl;
			PwmPin * pin = this->handleMap[handle];
			this->getPwmPin(pin);
			return pin->GetValue();
		}
		else
		{
			clog << kLogWarning << this->Name() << ": Attempt to call GetValue for non-existing handle '" << handle << "'" << endl;
			return 0;
		}
    }
    catch(FeatureNotImplementedException &x)
    {
        clog << kLogError << this->Name() << ": PWM Not implemented, but GetValue called nonetheless'" << handle << "'" << endl;
        return 0;
    }
}

double IoGroupHwPwm::GetMin(const std::string &handle)
{
    try
    {
		if(this->handleMap.count(handle) > 0)
		{
			clog << kLogDebug << this->Name() << ": Getting minimum value on handle '" << handle << "'" << endl;
			PwmPin * pin = this->handleMap[handle];
			this->getPwmPin(pin);
			return pin->GetMin();
		}
		else
		{
			clog << kLogWarning << this->Name() << ": Attempt to call GetMin for non-existing handle '" << handle << "'" << endl;
			return 0;
		}
    }
    catch(FeatureNotImplementedException &x)
    {
        clog << kLogError << this->Name() << ": PWM Not implemented, but GetMin called nonetheless'" << handle << "'" << endl;
        return 0;
    }
}

double IoGroupHwPwm::GetMax(const std::string &handle)
{
    try
    {
		if(this->handleMap.count(handle) > 0)
		{
			clog << kLogDebug << this->Name() << ": Getting maximum value on handle '" << handle << "'" << endl;
			PwmPin * pin = this->handleMap[handle];
			this->getPwmPin(pin);
			return pin->GetMax();
		}
		else
		{
			clog << kLogWarning << this->Name() << ": Attempt to call GetMax for non-existing handle '" << handle << "'" << endl;
			return 0;
		}
    }
    catch(FeatureNotImplementedException &x)
    {
        clog << kLogError << this->Name() << ": PWM Not implemented, but GetMax called nonetheless'" << handle << "'" << endl;
        return 0;
    }
}

// Frequency and timing functions
double IoGroupHwPwm::GetPwmPeriodMs()
{
	return this->pwmPeriodMs;
}

uint32_t IoGroupHwPwm::GetPwmFrequency()
{
	return this->pwmFrequency;
}

// SetPwmFrequency is protected, so subclasses can correct the pwm frequency to the proper value if needed
uint32_t IoGroupHwPwm::SetPwmFrequency(uint32_t frequency)
{
	if(frequency > 0)
	{
		this->pwmFrequency = frequency;
		this->pwmPeriodMs = (1.0/((double)frequency)) * 1000.0;
	}
}

std::set<IoGroupHwPwm::PwmPin*> IoGroupHwPwm::GetPwmPins()
{
	return this->pwmPins;
}

// private

bool IoGroupHwPwm::registerPwmPin(PwmPin * pin)
{
    if(this->handleMap.count(pin->GetHandle()) > 0)
    {
    	clog << "Registration error: handle alredy in use: " << pin->GetHandle() << endl;
        // handle already in use
        return false;
    }
    else if(this->idMap.count(pin->GetId()) > 0)
    {
    	clog << "Registration error: id alredy in use: " << pin->GetId() << endl;
        // id already in use
        return false;
    }
    else
    {
        this->handleMap[pin->GetHandle()] = pin;
        this->idMap[pin->GetId()] = pin;
        return true;
    }
}

uint16_t IoGroupHwPwm::getPinId(libconfig::Setting &io)
{
    string s;
    int32_t i;
    clog << "Determining pin id ... ";
    if(io.lookupValue("pin",i))
    {
    	clog << "found INT id '" << i << "' validating ..." << endl;

    	uint16_t id = this->getPinId(i);
    	return id;
    }
    else if(io.lookupValue("pin",s))
    {
    	clog << "found STRING id ... validating ..." << endl;
        return this->getPinId(s);
    }
    else
    {
    	throw IoPinInvalidException("No valid pin definition found in setting");
    }
}

// Functions that must be overridden in subclass

uint16_t IoGroupHwPwm::getPinId(const std::string &s)
{
    throw IoPinInvalidException("This subclass does not support string pin id's");
}
uint16_t IoGroupHwPwm::getPinId(const int32_t &i)
{
    throw IoPinInvalidException("This subclass does not support integer pin id's");
}

// Throws FeatureNotImplementedException unless overridden
void IoGroupHwPwm::setPwmPin(PwmPin * pin)
{
    throw FeatureNotImplementedException("PWM is not supported in this subclass");
}

// Throws FeatureNotImplementedException unless overridden
void IoGroupHwPwm::getPwmPin(PwmPin * pin)
{
    throw FeatureNotImplementedException("PWM is not supported in this subclass");
}

// Throws FeatureNotImplementedException unless overridden
void IoGroupHwPwm::preparePwmPin(PwmPin * pin)
{
    throw FeatureNotImplementedException("PWM is not supported in this subclass");
}

// Make sure these functions exist but do nothing in case a subclass doesn't need them.
void IoGroupHwPwm::beginConfig(libconfig::Setting &setting)
{
}

void IoGroupHwPwm::endConfig(void)
{
}


///////////////////////////////////////////////////////////////////////////////
// weighted_sum.hpp
//
//  Copyright 2006 Eric Niebler, Olivier Gygi. Distributed under the Boost
//  Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_ACCUMULATORS_STATISTICS_WEIGHTED_SUM_HPP_EAN_28_10_2005
#define BOOST_ACCUMULATORS_STATISTICS_WEIGHTED_SUM_HPP_EAN_28_10_2005

#include <boost/mpl/placeholders.hpp>
#include <boost/accumulators/framework/accumulator_base.hpp>
#include <boost/accumulators/framework/extractor.hpp>
#include <boost/accumulators/numeric/functional.hpp>
#include <boost/accumulators/framework/parameters/sample.hpp>
#include <boost/accumulators/framework/parameters/weight.hpp>
#include <boost/accumulators/framework/accumulators/external_accumulator.hpp>
#include <boost/accumulators/framework/depends_on.hpp>
#include <boost/accumulators/statistics_fwd.hpp>

namespace boost { namespace accumulators
{

namespace impl
{
    ///////////////////////////////////////////////////////////////////////////////
    // weighted_sum_impl
    template<typename Sample, typename Weight, typename Tag>
    struct weighted_sum_impl
      : accumulator_base
    {
        typedef typename numeric::functional::multiplies<Sample, Weight>::result_type weighted_sample;

        // for boost::result_of
        typedef weighted_sample result_type;

        template<typename Args>
        weighted_sum_impl(Args const &args)
          : weighted_sum_(
                args[parameter::keyword<Tag>::get() | Sample()]
                  * numeric::one<Weight>::value
            )
        {
        }

        template<typename Args>
        void operator ()(Args const &args)
        {
            // what about overflow?
            this->weighted_sum_ += args[parameter::keyword<Tag>::get()] * args[weight];
        }

        result_type result(dont_care) const
        {
            return this->weighted_sum_;
        }

        // make this accumulator serializeable
        template<class Archive>
        void serialize(Archive & ar, const unsigned int file_version)
        {
            ar & weighted_sum_;
        }

    private:

        weighted_sample weighted_sum_;
    };

} // namespace impl

///////////////////////////////////////////////////////////////////////////////
// tag::weighted_sum
//
namespace tag
{
    struct weighted_sum
      : depends_on<>
    {
        /// INTERNAL ONLY
        ///
        typedef accumulators::impl::weighted_sum_impl<mpl::_1, mpl::_2, tag::sample> impl;
    };

    template<typename VariateType, typename VariateTag>
    struct weighted_sum_of_variates
      : depends_on<>
    {
        /// INTERNAL ONLY
        ///
        typedef accumulators::impl::weighted_sum_impl<VariateType, mpl::_2, VariateTag> impl;
    };

    struct abstract_weighted_sum_of_variates
      : depends_on<>
    {
    };
}

///////////////////////////////////////////////////////////////////////////////
// extract::weighted_sum
//
namespace extract
{
    extractor<tag::weighted_sum> const weighted_sum = {};
    extractor<tag::abstract_weighted_sum_of_variates> const weighted_sum_of_variates = {};

    BOOST_ACCUMULATORS_IGNORE_GLOBAL(weighted_sum)
    BOOST_ACCUMULATORS_IGNORE_GLOBAL(weighted_sum_of_variates)
}

using extract::weighted_sum;
using extract::weighted_sum_of_variates;

template<typename VariateType, typename VariateTag>
struct feature_of<tag::weighted_sum_of_variates<VariateType, VariateTag> >
  : feature_of<tag::abstract_weighted_sum_of_variates>
{
};

}} // namespace boost::accumulators

#endif