#include <numeric> // for accumulate
#include <vector>
#include <algorithm>

#include <assert.h> // for assert? need C++ library?

#include <boost/filesystem.hpp>

#include "../include/Climate.h"

#include "inc/errorcode.h"
#include "inc/timeconst.h"

#include "TEMUtilityFunctions.h"
#include "TEMLogger.h"

extern src::severity_logger< severity_level > glg;

// Should really put these in temutil and then write some test
// routines that exercise the funcitons over a wide range of values

/** Find "Vapor Pressure Density"??? as a funciton of svp and vp ??
*/
float calculate_vpd (const float svp, const float vp) {
  float vpd = svp - vp;

  if (vpd < 0) {
    vpd = 0;
  }

  return vpd; // unit Pa
}

/** Find saturated vapor pressure as a function of temperature.

Guide to Meteorological Instruments and Methods of Observation (CIMO Guide)
  %      (WMO, 2008), for saturation vapor pressure
  %      (1) ew = 6.112 e(17.62 t/(243.12 + t))                [2]
  %      with t in [deg C] and ew in [hPa, mbar]

  %      (2) ei = 6.112 e(22.46 t/(272.62 + t))                [14]
  %      with t in [deg C] and ei in [hPa]
*/
float calculate_saturated_vapor_pressure(const float tair) {

  float svp; // saturated vapor pressure (Pa)

  if( tair > 0 ) {
    svp = 6.112 * exp(17.63 * tair / (243.12 + tair) ) * 100.0;
  } else {
    svp = 6.112 * exp(17.27 * tair / (272.62 + tair) ) * 100.0;
  }

  return svp;
}

/** Cloudiness as a function of girr and nirr */
float calculate_clouds(const float girr, const float nirr) {
  assert (nirr >= 0.0 && "Invalid nirr in Climate::calculate_clouds(..)!");
  assert (girr >= 0.0 && "Invalid girr in Climate::calculate_clouds(..)!");

  float clouds;

  if ( nirr >= (0.76 * girr) ) {
    clouds = 0.0;
  } else {
    clouds = 1.0 - (((nirr/girr) - 0.251)/0.509);
    clouds *= 100.0;
  }

  if ( clouds > 100.0 ) {
    clouds = 100.0;
  }

  return clouds;
}

/** PAR (watts per meter squared) as a function of cloudiness and nirr. */
float calculate_par(const float clds, const float nirr) {

  float par;  //  W/m2

  if ( clds >= 0.0 ) {
    par = nirr * ((0.2 * clds / 100.0) + 0.45);
  } else {
    par = MISSING_D;
  }

  return par;
}

/** The rain/snow split as a function of temperatore and precip.

Willmott's assumption. Returns a pair: (rain, snow).
*/
std::pair<float, float> willmot_split(const float t, const float p) {
  float r, s = 0.0;
  if ( t > 0.0 ) {
    r = p;
    s = 0.0;
  } else  {
    r = 0.0;
    s = p;
  }
  return std::make_pair(r,s);
}

/** GIRR (W/m^2) as a function of latitude and month.
*
*  More information and formulas can be found here:
*  http://www.fao.org/docrep/X0490E/x0490e07.htm
*  (section "Extraterrestrial radiation for daily periods")
*
*  And a table of expected values here:
*  http://www.fao.org/docrep/X0490E/x0490e0j.htm#annex%202.%20meteorological%20tables
*
*  NOTE: To convert from MJ/m^2/day to W/m^2:
*        multiply by (1,000,000)/(60*60*24) or ~11.57
*/
float calculate_girr(const float lat, const int im) {
  const float pi = 3.141592654;                // Greek "pi" TODO: fix this to use constant from math library?
  const float sp = 1368.0 * 3600.0 / 41860.0;  // solar constant
  float lambda;
  float sumd;
  float sig;
  float eta;
  float sinbeta;
  float sb;
  float sotd;
  int hour;
  lambda = lat * pi / 180.0;
  float gross = 0.0;

  for ( int day = 0; day < DINM[im]; ++day ) {

    // find julian day
    // http://www.fao.org/docrep/X0490E/x0490e0j.htm#annex%202.%20meteorological%20tables
    float jd = 0.0;
    jd = (275 * (im+1)/9) - 30 + (day+1);
    if( (im+1) < 3 ) {
      jd = jd + 2;
    }
    int julianday = int(jd);

    sumd = 0;
    sig = -23.4856*cos(2 * pi * (julianday + 10.0)/365.25);
    sig *= pi / 180.0;

    for ( hour = 0; hour < 24; hour++ ) {
      eta = (float) ((hour+1) - 12) * pi / 12.0;
      sinbeta = sin(lambda)*sin(sig) + cos(lambda)*cos(sig)*cos(eta);
      sotd = 1 - (0.016729 * cos(0.9856 * (julianday - 4.0)
                                 * pi / 180.0));
      sb = sp * sinbeta / pow((double)sotd,2.0);

      if (sb >= 0.0) {
        sumd += sb;
      }
    }

    gross += sumd;
  }

  gross /= (float)DINM[im];
  gross *= 0.484; // convert from cal/cm2day to W/m^2
  return gross;
}

std::vector<float> calculate_daily_prec(const int midx, const float mta, const float mprec) {

  // input are monthly precipitation, monthly temperature
  // output are daily precpitation
  // this function is based on the code provided Qianlai on Feb. 19, 2007

  float RT, RS, R ;
  RT=1.778;
  RS=0.635;
  R=0.5;
  float TEMP, PREC, DURT, DURS;
  PREC = mprec/10.0/2.54; //comvert mm to cm, then to in.
  DURT=RT/R;
  DURS=RS/R;
  float B=1.0, T=0.0, S=1.0, RB, DURB;

  float RAINDUR[DINM[midx]];
  float RAININTE[DINM[midx]];
  for(int id = 0; id < DINM[midx]; id++) {
    RAININTE[id] =0.;
    RAINDUR[id] = 0.;
  }

  TEMP = mta;

  //  Case 1, TEMP<0.
  if (TEMP <= 0.0) {
    if (PREC <= 1.0) {
      B=1.0;
      T=0.0;
      S=1.0;
    } else {
      B=1.0;
      T=1.0;
      S=1.0;
    }
  }
  //   Case 2, PREC<1.0 inch.
  else if (PREC <= 1.0) {
    B=1.0;
    T=0.0;
    S=1.0;
  }
  //   Case 3, 1.0<PREC<2.5 inches.
  else if ((2.5 >= PREC) && (PREC > 1.0)) {
    B=1.0;
    T=1.0;
    S=1.0;
  }
  //   Case 4, 2.5<PREC<4.0 inches.
  else if ((4.0 >= PREC) && (PREC > 2.5)) {
    B=1.0;
    S=4.0;

    if (PREC < 3.7) {
      T=1.0;
    } else {
      T=2.0;
    }
  }
  //   Case 5, 4.0<PREC<5.0 inches.
  else if ((5.0 >= PREC) && (PREC > 4.0)) {
    B=1.0;
    S=4.0;

    if (PREC < 4.43) {
      T=1.0;
    } else {
      T=2.0;
    }
  }
  //   Case 6, 5.0<PREC<7.0 inches.
  else if ((7.0 >= PREC) && (PREC > 5.0)) {
    B=2.0;
    S=4.0;

    if (PREC < 5.65) {
      T=1.0;
    } else {
      T=2.0;
    }
  }
  //   Case 7, 7.0<PREC<9.0 inches.
  else if ((9.0 >= PREC) && (PREC > 7.0)) {
    B=2.0;
    S=6.0;

    if (PREC < 8.21) {
      T=3.0;
    } else {
      T=4.0;
    }
  }
  //   Case 8, 9.0<PREC<11.0 inches.
  else if ((11.0 >= PREC) && (PREC > 9.0)) {
    B=3.0;
    S=6.0;

    if (PREC < 10.0) {
      T=4.0;
    } else {
      T=5.0;
    }
  }
  //   Case 9, PREC>11.0 inches.
  else if (PREC > 11.0) {
    B=4.0;
    S=7.0;

    if (PREC < 13.0) {
      T=4.0;
    } else {
      T=5.0;
    }
  }

  RB = ( PREC*2.54 - RS*S - RT*T ) / B;   // Yuan
  DURB = RB / R;    // Yuan

  if (DURB <= 0.01) {
    DURB = 0.01;  // !added //changed from zero to 0.01 by shuhua
  }

  PREC = PREC * 2.54 * 10.0;  // convert back to cm, and then to mm
  float BB, TT;
  int KTT, KDD, KTD, KKTD;
  int NN, DT;
  DT = DINM[midx];
  KTT = (int)(B+T);
  KTD = DT / KTT;
  KDD = DT - KTT * KTD;
  BB = B;
  TT = T;
  NN = 0;

  for (int JJ=1; JJ<=KTT; JJ++) {
    if (BB > 0.0) {
      BB = BB - 1.0;

      for (int L=1; L<=KTD; L++) {
        NN = NN+1;
        RAININTE[NN] = 0.0;
        RAINDUR[NN] = 0.0;

        if (L == KTD) {
          RAININTE[NN] = 5.0; // unit with mm /hr
          RAINDUR[NN] = DURB;
        }
      }
    }

    if (TT > 0.0) {
      TT = TT - 1.0;

      if (JJ == 1) {
        KKTD = KTD+KDD;
      } else {
        KKTD = KTD;
      }

      for (int L=1; L <= KKTD; L++) {
        NN = NN+1;
        RAININTE[NN] = 0.0;
        RAINDUR[NN] = 0.0;

        if (L == KKTD) {
          RAININTE[NN] = 5.0; //unit mm/hr
          RAINDUR[NN] = DURT;
        }
      }
    }
  }  // end of for J

  // in winter season, DURT was always zero, so put the precipitation
  //   on the day with RAININTE>0;
  int numprec = 0;
  double tothour = 0.;

  for (int id = 0; id < DINM[midx]; id++) {
    if (RAINDUR[id+1] > 0) {
      numprec++;
      tothour += RAINDUR[id+1];
    }
  }

  float sumprec = 0.;

  std::vector<float> precip_daily (DINM[midx], 0);
  if(numprec > 0) {
    double rainrate = mprec / tothour;

    for (int id = 0; id < DINM[midx]; id++) {
      precip_daily[id] = RAINDUR[id+1] * rainrate;
      sumprec += precip_daily[id];
    }
  }

  return precip_daily;
}

Climate::Climate() {
  BOOST_LOG_SEV(glg, note) << "--> CLIMATE --> empty ctor";
}

Climate::Climate(const std::string& fname, const std::string& co2fname, int y, int x) {
  BOOST_LOG_SEV(glg, note) << "--> CLIMATE --> BETTER CTOR";
  this->load_from_file(fname, y, x);

  // co2 is not spatially explicit
  #pragma omp critical(load_input)
  {
    this->co2 = temutil::get_timeseries(co2fname, "co2");
  }
}

void Climate::load_from_file(const std::string& fname, int y, int x) {

  if(!boost::filesystem::exists(fname)){
    BOOST_LOG_SEV(glg, fatal) << "Input file "<<fname<<" does not exist";
  }

  #pragma omp critical(load_input)
  {
    BOOST_LOG_SEV(glg, info) << "Loading climate from file: " << fname;
    BOOST_LOG_SEV(glg, info) << "Loading climate for (y, x) point: "
                             << "(" << y <<","<< x <<"), all timesteps.";

    BOOST_LOG_SEV(glg, info) << "Read in the base climate data timeseries ...";

    tair = temutil::get_timeseries<float>(fname, "tair", y, x);
    vapo = temutil::get_timeseries<float>(fname, "vapor_press", y, x);
    prec = temutil::get_timeseries<float>(fname, "precip", y, x);
    nirr = temutil::get_timeseries<float>(fname, "nirr", y, x);
  }//End critical(load_climate)

  // Report on sizes...
  BOOST_LOG_SEV(glg, info) << "  -->sizes (tair, vapor_press, precip, nirr): ("
                           << tair.size() << ", " << vapo.size() << ", "
                           << prec.size() << ", " << nirr.size() << ")";

  // assert all sizes are the same?
  if ( !(tair.size() == prec.size() &&
         tair.size() == vapo.size() &&
         tair.size() == nirr.size()) ) {
    BOOST_LOG_SEV(glg, err) << "ERROR - your base climate datasets are not "
                            << "the same size! Very little bounds checking "
                            << "done, not sure what will happen.";

  }

  // make some space for the derived variables
  girr = std::vector<float>(12, 0); // <-- !! wow, no need for year dimension??
  par = std::vector<float>(prec.size(), 0);
  cld = std::vector<float>(prec.size(), 0);

  BOOST_LOG_SEV(glg, debug) << "tair = [" << temutil::vec2csv(tair) << "]";
  BOOST_LOG_SEV(glg, debug) << "prec = [" << temutil::vec2csv(prec) << "]";

  // find girr as a function of month and latitude
  std::pair<float, float> latlon = temutil::get_latlon(fname, y, x);
  for (int im = 0; im < 12; ++im) {
    float g = calculate_girr(latlon.first, im);
    girr[im] = g;
  }
  BOOST_LOG_SEV(glg, debug) << "nirr = [" << temutil::vec2csv(nirr) << "]";
  BOOST_LOG_SEV(glg, debug) << "girr = [" << temutil::vec2csv(girr) << "]";

  // determine "cloudiness" based on ratio of girr and nirr
  for (int i = 0; i < nirr.size(); ++i) {
    int midx = i % 12;
    cld[i] = calculate_clouds(girr[midx], nirr[i]);
  }
  BOOST_LOG_SEV(glg, debug) << "cld = [" << temutil::vec2csv(cld) << "]";

  // find par based on cloudiness and nirr
  for (int i = 0; i < cld.size(); ++i) {
    par[i] = calculate_par(cld[i], nirr[i]);
  }
  BOOST_LOG_SEV(glg, debug) << "par = [" << temutil::vec2csv(par) << "]";

  // create the simplified climate by averaging the first X years of data
  avgX_tair = avg_over(tair, 30);
  avgX_prec = avg_over(prec, 30);
  avgX_nirr = avg_over(nirr, 30);
  avgX_vapo = avg_over(vapo, 30);

  // Do we need simplified 'avgX_' values for par, and cld??
  // ===> YES: the derived variables should probably be based off the avgX
  //      containers...

  // Finally, need to create the daily dataset(s) by interpolating the monthly
  // --> actually looking like these should not be calculated upon construction.
  //     instead, they should get calculated each year...

}

/** This loads data from a projected climate data file, overwriting any old climate data*/
void Climate::load_proj_climate(const std::string& fname, int y, int x){
  BOOST_LOG_SEV(glg, note) << "Climate, loading projected data";

  this->load_from_file(fname, y, x);
}

std::vector<float> Climate::avg_over(const std::vector<float> & var, const int window) {

  assert(var.size() % 12 == 0 && "The data vector is the wrong size! var.size() should be an even multiple of 12.");
  assert(var.size() >= 12*window && "The data vector is too short to average over the window!");

  // make space for the result - one number for each month
  std::vector<float> result(12, 0);

  for (int im = 0; im < 12; ++im) {
    // make space for a month's data over the averaging window
    std::vector<float> mdata(window, 0);

    // gather up the data for this month over the averaging window
    for (int iy = 0; iy < window; ++iy) {
      mdata[iy] = var[iy*12 + im];
    }

    // average the data for the month
    float sum = std::accumulate(mdata.begin(), mdata.end(), 0.0);

    // put the value in the result vector for this month
    result[im] = sum / window; // ?? should window be a float??

    //BOOST_LOG_SEV(glg, debug) << "result = [" << temutil::vec2csv(result) << "]";

  }

  BOOST_LOG_SEV(glg, debug) << "result = [" << temutil::vec2csv(result) << "]";

  return result;

}

// Interpolate from monthly values to daily. Does NOT account for leap years!
std::vector<float> Climate::monthly2daily(const std::vector<float>& mly_vals) {

  // setup a container for the daily data
  std::vector<float> daily_container;

  // set up the "month midpoint to relative days" vector
  static const float arr[] = { -15.5, 15.5, 45.0, 74.5, 105.0,
                                135.5, 166, 196.5, 227.5, 258, 288.5,
                                319, 349.5, 380.5 };
  std::vector<float> rel_days;
  rel_days.assign( arr, arr + sizeof(arr) / sizeof(arr[0]) );

  assert(mly_vals.size() == 14 && "Monthly values must be size 14 (D J F M A M J J A S O N D J)");
  assert(rel_days.size() == 14 && "Relative days vector must be size 14: (D J F M A M J J A S O N D J)");

  for (std::vector<float>::iterator it = rel_days.begin()+1; it != rel_days.end(); ++it) {
    int idx = it - rel_days.begin();

    // find our range to work over
    float x0 = *(it-1);
    float x1 = *it;

    std::vector<float> psd = temutil::resample(
        std::make_pair( x0, mly_vals.at(idx-1) ),   // first point on line
        std::make_pair( x1, mly_vals.at(idx) ),                // second point on line
        int(floor(x0)),     // begining of interval to interpolate
        int(floor(x1)),     // end of iterval to interpolate
        1                   // step size
    );

    // Add this month's interpolated values to the back of the
    // temporary storage...
    daily_container.insert(daily_container.end(), psd.begin(), psd.end());
  }

  // TODO: Probably need to fix this? mostly works, but returns a container
  // that has 366 elements, even on non-leap years. Also when plotting, there
  // appears to be a slight discontinutiy in the interpolation from month to month
  std::vector<float> cal_yr_daily(daily_container.begin()+16, daily_container.end()-14);

  return cal_yr_daily;
}

// rough draft method to get "prev" Dec, this year, and "next" Jan that are
// needed for monthly2daily interpolation...
std::vector<float> Climate::eq_range(const std::vector<float>& data) {
  std::vector<float> foo;

  // recycle Dec as the "previous" Dec
  foo.push_back(data.at(11));

  // get Jan - Dec values
  foo.insert(foo.end(), data.begin(), data.begin()+12);

  // use this Jan as "next" Jan
  foo.push_back(data.at(0));

  return foo;
}

/** Method to build a vector of 14 monthly data points for interpolation.
* In order to interpolate out to the ends of the year, you need the 12 months
* of data, plus the preceeding Dec and following Jan.
*/
std::vector<float> Climate::interpolation_range(const std::vector<float>& data, int year){
  //BOOST_LOG_SEV(glg, fatal) << "interpolation_range, year: "<<year;

  std::vector<float> foo;

  int curr_jan = year*12;

  // Copy in previous Dec, unless in year 0
  if(year==0){
    foo.push_back(data.at(11));
  }
  else{
    foo.push_back(data.at(curr_jan-1));
  }

  // Get Jan - Dec values
  foo.insert(foo.end(), &data[curr_jan], &data[curr_jan+12]);

  // Copy in next Jan, unless it is the last year's worth of data
  if(year == data.size()/12-1){
    foo.push_back(data.at(curr_jan));
  }
  else{
    foo.push_back(data.at(curr_jan+13));
  }

  return foo;
}

/** Prepares a single year of daily driving data */
void Climate::prepare_daily_driving_data(int iy, const std::string& stage) {
  //FIX rename iy to avoid confusion, since it isn't always the same
  //as the iy in Runner (SP passes in a modded value).

  if( (stage.find("pre") != std::string::npos)
      || (stage.find("eq") != std::string::npos) ){
    //Uses the same value of CO2 every day of the year.
    //Pre-Run and EQ also use constant CO2 value for all years.
    co2_d = co2.at(0);

    //Create daily data by interpolating
    tair_d = monthly2daily(eq_range(avgX_tair));
    vapo_d = monthly2daily(eq_range(avgX_vapo));
    nirr_d = monthly2daily(eq_range(avgX_nirr));

    //Not totally sure if this is right to interpolate (girr and par)
    par_d = monthly2daily(eq_range(par));
  }
  else{//Spin-up, Transient, Scenario
    //Uses the same value of CO2 every day of the year
    co2_d = co2.at(iy);

    //Create daily data by interpolating
    // straight up interpolated....
    tair_d = monthly2daily(interpolation_range(tair, iy));
    vapo_d = monthly2daily(interpolation_range(vapo, iy));
    nirr_d = monthly2daily(interpolation_range(nirr, iy));

    //Not totally sure if this is right to interpolate (girr and par)
    par_d = monthly2daily(interpolation_range(par, iy));
  }

  //BOOST_LOG_SEV(glg, debug) << stage << " tair_d = [" << temutil::vec2csv(tair_d) << "]";

  //Not totally sure if this is right to interpolate (girr and par)
  //GIRR is passed to eq_range for all stages as it has only twelve values.
  girr_d = monthly2daily(eq_range(girr));

  // The interpolation is slightly broken, so it 'overshoots' when the
  // slope is negative, and can result in negative values.
  BOOST_LOG_SEV(glg, info) << "Forcing negative values to zero in girr and nirr daily containers...";
  std::for_each(nirr_d.begin(), nirr_d.end(), temutil::force_negative2zero);
  std::for_each(girr_d.begin(), girr_d.end(), temutil::force_negative2zero);

  // much more complicated than straight interpolation...
  prec_d.clear();
  for (int i=0; i < 12; ++i) {
    std::vector<float> v;
    if( (stage.find("pre") != std::string::npos)
        || (stage.find("eq") != std::string::npos) ){
      v = calculate_daily_prec(i, avgX_tair.at(i), avgX_prec.at(i));
    }
    else{//Spin-Up, Transient, Scenario
      v = calculate_daily_prec(i, tair.at(i), prec.at(i));
    }

    prec_d.insert( prec_d.end(), v.begin(), v.end() );
  }

  // derive rain and snow from precip...
  // Look into boost::zip_iterator
  rain_d.clear();
  snow_d.clear();
  for (int i = 0; i < prec_d.size(); ++i) {
    std::pair<float, float> rs = willmot_split(tair_d[i], prec_d[i]);
    rain_d.push_back(rs.first);
    snow_d.push_back(rs.second);
  }

  svp_d.resize(tair_d.size());
  std::transform( tair_d.begin(), tair_d.end(), svp_d.begin(), calculate_saturated_vapor_pressure );

  vpd_d.resize(tair_d.size());
  std::transform( svp_d.begin(), svp_d.end(), vapo_d.begin(), vpd_d.begin(), calculate_vpd );

  cld_d.resize(tair_d.size());
  std::transform( girr_d.begin(), girr_d.end(), nirr_d.begin(), cld_d.begin(), calculate_clouds );

  // THESE MAY NEVER BE USED??
  // rhoa_d;
  // dersvp_d;
  // abshd_d;

  // Dump data to log stream for debugging analysis
  //this->dailycontainers2log();
}

/** Print the contents of the monthly containers to the log stream.
* Format is intendend to be copy/pastable into python.
*/
void Climate::monthlycontainers2log() {
  BOOST_LOG_SEV(glg, debug) << "co2 = [" << temutil::vec2csv(co2) << "]";
  BOOST_LOG_SEV(glg, debug) << "tair = [" << temutil::vec2csv(tair) << "]";
  BOOST_LOG_SEV(glg, debug) << "prec = [" << temutil::vec2csv(prec) << "]";
  BOOST_LOG_SEV(glg, debug) << "nirr = [" << temutil::vec2csv(nirr) << "]";
  BOOST_LOG_SEV(glg, debug) << "vapo = [" << temutil::vec2csv(vapo) << "]";
  BOOST_LOG_SEV(glg, debug) << "girr = [" << temutil::vec2csv(girr) << "]";
  BOOST_LOG_SEV(glg, debug) << "cld = [" << temutil::vec2csv(cld) << "]";
  BOOST_LOG_SEV(glg, debug) << "par = [" << temutil::vec2csv(par) << "]";
}

/** Print the contents of the daily containers to the log stream.
* Format is intendend to be copy/pastable into python.
*/
void Climate::dailycontainers2log() {

    BOOST_LOG_SEV(glg, debug) << "tair_d = [" << temutil::vec2csv(tair_d) << "]";
    BOOST_LOG_SEV(glg, debug) << "nirr_d = [" << temutil::vec2csv(nirr_d) << "]";
    BOOST_LOG_SEV(glg, debug) << "vapo_d = [" << temutil::vec2csv(vapo_d) << "]";
    BOOST_LOG_SEV(glg, debug) << "prec_d = [" << temutil::vec2csv(prec_d) << "]";
    BOOST_LOG_SEV(glg, debug) << "rain_d = [" << temutil::vec2csv(rain_d) << "]";
    BOOST_LOG_SEV(glg, debug) << "snow_d = [" << temutil::vec2csv(snow_d) << "]";
    BOOST_LOG_SEV(glg, debug) << "svp_d = [" << temutil::vec2csv(svp_d) << "]";
    BOOST_LOG_SEV(glg, debug) << "vpd_d = [" << temutil::vec2csv(vpd_d) << "]";
    BOOST_LOG_SEV(glg, debug) << "girr_d = [" << temutil::vec2csv(girr_d) << "]";
    BOOST_LOG_SEV(glg, debug) << "cld_d = [" << temutil::vec2csv(cld_d) << "]";
    BOOST_LOG_SEV(glg, debug) << "par_d = [" << temutil::vec2csv(par_d) << "]";

    BOOST_LOG_SEV(glg, debug) << "tair_d.size() = " << tair_d.size();
    BOOST_LOG_SEV(glg, debug) << "nirr_d.size() = " << nirr_d.size();
    BOOST_LOG_SEV(glg, debug) << "vapo_d.size() = " << vapo_d.size();
    BOOST_LOG_SEV(glg, debug) << "prec_d.size() = " << prec_d.size();
    BOOST_LOG_SEV(glg, debug) << "rain_d.size() = " << rain_d.size();
    BOOST_LOG_SEV(glg, debug) << "snow_d.size() = " << snow_d.size();
    BOOST_LOG_SEV(glg, debug) << "vpd_d.size() = " << vpd_d.size();
    BOOST_LOG_SEV(glg, debug) << "svp_d.size() = " << svp_d.size();
    BOOST_LOG_SEV(glg, debug) << "girr_d.size() = " << girr_d.size();
    BOOST_LOG_SEV(glg, debug) << "cld_d.size() = " << cld_d.size();
    BOOST_LOG_SEV(glg, debug) << "par_d.size() = " << par_d.size();

}


#include <string>
#include <map>
#include <functional>
#include <Eigen/Dense>
#include "two_layer_net.h"
#include "simple_activation.h"
#include "simple_loss.h"

#include <iostream>

namespace MyDL
{

    using namespace Eigen;
    using std::cout;
    using std::endl;

    // デフォルトコンストラクタ(初期値は適当)
    TwoLayerNet::TwoLayerNet() : _input_size(3), _hidden_size(3), _output_size(2), _weight_init_std(0.01)
    {
        // paramsに格納する変数の初期化
        MatrixXd W1 = _weight_init_std * MatrixXd::Random(_input_size, _hidden_size);
        VectorXd b1 = VectorXd::Zero(_hidden_size);
        MatrixXd W2 = _weight_init_std * MatrixXd::Random(_hidden_size, _output_size);
        VectorXd b2 = VectorXd::Zero(_output_size);

        params["W1"] = W1;
        params["W2"] = W2;
        params["b1"] = b1;
        params["b2"] = b2;
    }

    // 初期値ありのコンストラクタ
    TwoLayerNet::TwoLayerNet(int input_size, int hidden_size, int output_size, double weight_init_std) : _input_size(input_size), _hidden_size(hidden_size), _output_size(output_size), _weight_init_std(weight_init_std)
    {
        // paramsに格納する変数の初期化
        MatrixXd W1 = _weight_init_std * MatrixXd::Random(_input_size, _hidden_size);
        VectorXd b1 = VectorXd::Zero(_hidden_size);
        MatrixXd W2 = _weight_init_std * MatrixXd::Random(_hidden_size, _output_size);
        VectorXd b2 = VectorXd::Zero(_output_size);

        params["W1"] = W1;
        params["W2"] = W2;
        params["b1"] = b1;
        params["b2"] = b2;
    }

    MatrixXd TwoLayerNet::predict(MatrixXd &X)
    {
        MatrixXd a1, a2, z1, y, W1, W2;
        VectorXd b1, b2;
        W1 = params["W1"];
        W2 = params["W2"];
        b1 = params["b1"];
        b2 = params["b2"];

        // ブロードキャスト演算をするように実装(numpyとは仕様が違うことに注意)
        a1 = (X * W1).rowwise() + b1.transpose();
        z1 = sigmoid(a1);
        a2 = (z1 * W2).rowwise() + b2.transpose();
        y = softmax(a2);

        _cache["a1"] = a1;
        _cache["z1"] = z1;
        _cache["a2"] = a2;
        _cache["y"] = y;

        return y;
    }

    double TwoLayerNet::loss(MatrixXd &x, MatrixXd &t)
    {
        MatrixXd y;
        y = this->predict(x);

        double loss;
        loss = MyDL::cross_entropy_error(y, t);
        return loss;
    }

    double TwoLayerNet::accuracy(MatrixXd& x, MatrixXd& t){
        MatrixXd y;
        MatrixXd::Index y_row, y_col, t_row, t_col;

        double accuracy = 0;
        int batch_size = t.rows();
        // double max_y, max_t;

        y = this->predict(x);

        // 各行ごとに、最大要素のインデックスを取得 → インデックスが等しければ、accuracyに加算
        for (int i=0; i < batch_size; i++){
            y.row(i).maxCoeff(&y_row, &y_col);
            t.row(i).maxCoeff(&t_row, &t_col);

            accuracy += (double)(y_col == t_col); // カラムのインデックスだけ見ればOK
        }

        return accuracy / batch_size;
    }

    // 数値微分では遅すぎるので、誤差逆伝播法を実装
    std::map<std::string, MatrixXd> TwoLayerNet::gradient(MatrixXd& X, MatrixXd& t){
        using std::map;
        using std::string;

        map<string, MatrixXd> grads;
        MatrixXd dW1, dW2;
        VectorXd db1, db2;

        MatrixXd da2, da1, dz1;
        MatrixXd y, z1, a1, a2, W1, W2, b1, b2;
        int batch_size = t.rows();

        y  = this->predict(X); // これをコールしておかないと、_cacheの各種変数が保存されない
        a1 = _cache["a1"];
        a2 = _cache["a2"];
        z1 = _cache["z1"];
        W1 = params["W1"];
        W2 = params["W2"];
        b1 = params["b1"];
        b2 = params["b2"];

        // 逆伝播計算
        // softmax with loss layer
        da2 = (y - t) / batch_size;
        // affine layer 2
        dz1 = da2 * W2.transpose();
        dW2 = z1.transpose() * da2; // 縦ベクトル × 横ベクトル の構図(バッチ方向に縮約)
        db2 = da2.colwise().sum();
        // sigmoid layer: da1 = z1(1-z1) * dz1
        da1 = z1.array() * (MatrixXd::Ones(z1.rows(), z1.cols()) - z1).array() * dz1.array();
        // affine layer 1
        dW1 = X.transpose() * da1;
        db1 = da1.colwise().sum();
        // dx = da1 * W2.transpose() // → 今回は必要ないのでスルー

        grads["W1"] = dW1;
        grads["W2"] = dW2;
        grads["b1"] = db1;
        grads["b2"] = db2;

        return grads;
    }

}


/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * File:   polynomial_selection.cpp
 * Author: pankaj
 *
 * Created on 20 February, 2016, 11:40 AM
 */

#include <cstdlib>

#include <iostream>
#include<string>
#include<NTL/tools.h>
#include<NTL/vector.h>
#include<NTL/matrix.h>
#include<NTL/ZZX.h>
#include<NTL/mat_ZZ.h>
#include<NTL/GF2X.h>
#include <NTL/pair_GF2X_long.h>
#include<NTL/GF2XFactoring.h>
#include<NTL/GF2E.h>
#include<NTL/mat_GF2E.h>
#include<math.h>
#include"ffspol.h"
#include"ffstools.h"
#include<string>

//Resultant by sylvester's matrix method
void resultant(GF2X& r,const ffs_poly& f,const ffs_poly& g)
{
  GF2X p=BuildSparseIrred_GF2X(1000); //set some large bound of degree for sparse irreducible polynomial which will not affect the computation
  GF2E::init(p);

  Mat<GF2E> M;
 long d=f.deg+g.deg;

 M.SetDims(d,d);
 long n=f.deg;
 long m=g.deg;
 int i,j;

//Initialize the matrix with zero's
 for(i=0;i<d;i++)
 for(j=0;j<d;j++)
  M[i][j]=conv<GF2E>(0);

for(i=0;i<m;i++)
 for(j=0;j<d;j++)
{
   if(i>=0 && m>i)
  {
   M[i][j]=conv<GF2E>(f.coeffs[n+i-j]);

  }
}

 for(;i<d;i++)
 for(j=0;j<d;j++)
{
   if((i-j)>=0 && (i-j)<2)
   {
     M[i][j]=conv<GF2E>(g.coeffs[i-j]);
   }
}
r=conv<GF2X>(determinant(M));
}

ffs_poly polynomial_selection(const ffs_poly& f,const long n)
{
   ffs_poly g;
   long t=2;
   Vec< Pair< GF2X,long > > factors;
   // vec_GF2X factors;
   factors.SetLength(100);
   long d=ceil(n/f.deg);
   g.deg=1;
  g.coeffs.SetLength(t);

   GF2X g0,g1;
   GF2X res;

  while(1)
  {
     g0=random_GF2X(d+1);
     g1=random_GF2X(d+1);

     cout<<"g0="<<g0<<endl;
     cout<<"g0="<<g0<<endl;

     g.coeffs[0]=g0;
     g.coeffs[1]=g1;
     resultant(res,f,g);

    cout<<"\n\nResulant="<<res<<endl;

    CanZass(factors,res,(long)0); //defined in GF2XFactoring.h

    cout<<"Factors="<<factors<<endl;

    for(int i=0;i<factors.length();i++)
    {
     if(IterIrredTest(factors[i].a) && (deg(factors[i].a)==n))
     {
       cout<<"Irreducible factor of degree n:-"<<factors[i].a<<endl;
       return g;
     }
   }

 }

}

int main()
{
  ffs_poly f,g;
  long n=607;
  string pol_f="4,0,3,0,0,1";
  cout<<"\n polynomial f:\n";
  read_ffs_poly(f,pol_f);
  print_ffs_poly(f);
  cout<<"\nPolynomial g:\n";
  g=polynomial_selection(f,n);
  print_ffs_poly(g);
  return 0;
}


#include <iostream>
#include <cmath>
#include <cassert>
#include <arprec/mp_real.h>
#include "Polynomklasse_templ.hpp"
#include <boost/numeric/mtl/mtl.hpp>

using namespace std;

class mp_starter
{
  public:
    mp_starter() { mp::mp_init(40); }
    ~mp_starter() { mp::mp_finalize(); }
};

const mp_starter dummy;

int const gp=10; //=N+1
mp_real gitter[gp];
mp_real e("1.0");

polynom<mp_real> phi(int i, mp_real eins, mp_real zwei){
	polynom<mp_real> c(1);
	if(i==0||i==gp-1){
		c.data[0]=0.0;
		c.data[1]=0.0;
		return c;
	}
	if(eins==gitter[i] && zwei==gitter[i+1]){
		c.data[0]=-gitter[i+1]/(gitter[i]-gitter[i+1]);
		c.data[1]=1/(gitter[i]-gitter[i+1]);
	}else if(eins==gitter[i-1] && zwei==gitter[i]){
		c.data[0]=-gitter[i-1]/(gitter[i]-gitter[i-1]);
		c.data[1]=1/(gitter[i]-gitter[i-1]);
	}else{
		c.data[0]=0.0;
		c.data[1]=0.0;
	}
	return c;
}

mp_real func_c(mp_real x){
	return 1.0;
}

mp_real f(mp_real x){
	return 1.0;
}

polynom<mp_real> inter_c(int i, mp_real eins, mp_real zwei){
	polynom<mp_real> c(1);
	if(i==0||i==gp-1){
		c.data[0]=0.0;
		c.data[1]=0.0;
		return c;
	}
	if(eins==gitter[i] && zwei==gitter[i+1]){
		c.data[0]=func_c(gitter[i])*(-1.0)*(gitter[i+1])/(gitter[i]-gitter[i+1]);
		c.data[1]=func_c(gitter[i])/(gitter[i]-gitter[i+1]);
	}else if(eins==gitter[i-1] && zwei==gitter[i]){
		c.data[0]=func_c(gitter[i])*(-1.0)*(gitter[i-1])/(gitter[i]-gitter[i-1]);
		c.data[1]=func_c(gitter[i])/(gitter[i]-gitter[i-1]);
	}else{
		c.data[0]=0.0;
		c.data[1]=0.0;
	}
	return c;
}
polynom<mp_real> inter_f(mp_real eins, mp_real zwei){
	polynom<mp_real> c(1);
	c.data[0]=f(eins)-(f(zwei)-f(eins))/(zwei-eins)*eins;
	c.data[1]=(f(zwei)-f(eins))/(zwei-eins);
	return c;

}

mp_real eintrag(int i, int j){
	mp_real tmp=0.0;
	for(int k=0;k<gp-1;k++){
		polynom<mp_real> da=phi(i,gitter[k],gitter[k+1]).diff();
		polynom<mp_real> db=phi(j,gitter[k],gitter[k+1]).diff();
		polynom<mp_real> b=phi(j,gitter[k],gitter[k+1]);
		polynom<mp_real> I_ca=inter_c(i,gitter[k],gitter[k+1]);
		tmp+=(e*e)*(da*db).integral(gitter[k],gitter[k+1])+(I_ca*b).integral(gitter[k],gitter[k+1]);
	}
	return tmp;

}

mp_real vektoreintrag(int j){
	mp_real tmp=0.0;
	for(int k=0;k<gp-1;k++){
		polynom<mp_real> b=phi(j,gitter[k],gitter[k+1]);
		polynom<mp_real> I_f=inter_f(gitter[k],gitter[k+1]);
		tmp+=(I_f*b).integral(gitter[k],gitter[k+1]);
	}
	return tmp;
}

int main(){
	cout<<"test";
	// mp::mp_init(40);
	cout<<"test1";
	for (int i=0;i<gp;i++){
		cout<<"test2";
		gitter[i]=1.0*i/(gp-1);
		cout<<"test3";
		cout<<"gitter["<<i<<"]="<<gitter[i]<<" \n";

	}

	cout.precision(3);

	mtl::dense2D<mp_real> matrix(gp-2,gp-2);

	for(int i=1;i<gp-1;i++){
		for(int j=1;j<gp-1;j++){
			matrix[i-1][j-1]=eintrag(i,j);

		}
	}

	cout<<matrix;

	mtl::dense_vector<mp_real> vector(gp-2),p(gp-2);

	for(int i=1;i<gp-1;i++){
		vector[i-1]=vektoreintrag(i);
	}
	cout<<vector<<"\n";

	p=lu_solve(matrix,vector);

	cout<<p<<"\n";

	return 0;

}


#include "stdafx.h"

#include <iostream>
#include <stdio.h>

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include <Math.h>
#include <Eigen/Geometry>
#include <Eigen/StdVector>

#include "utils.h"

using namespace Eigen;
using namespace std;
using namespace cv;

const float LIMBUS_R_MM = 6;

// ThinkpadHelix Params
const double FOCAL_LEN_X_PX = 957.648052597;
const double FOCAL_LEN_Y_PX = 960.154605354;
const double FOCAL_LEN_Z_PX = (FOCAL_LEN_X_PX + FOCAL_LEN_Y_PX) / 2;
const cv::Point2d PRIN_POINT(634.799023712, 367.91715841);

vector<Vector3d> ellipse_to_limbus(cv::RotatedRect ellipse, bool limbus_switch=true){

	vector<Vector3d> limbus_to_return;

	double maj_axis_px = ellipse.size.width, min_axis_px = ellipse.size.height;

	// Using iris_r_px / focal_len_px = iris_r_mm / distance_to_iris_mm
	double iris_z_mm = (LIMBUS_R_MM * 2 * FOCAL_LEN_Z_PX) / maj_axis_px;

    // Using (x_screen_px - prin_point) / focal_len_px = x_world / z_world
	double iris_x_mm = -iris_z_mm * (ellipse.center.x - PRIN_POINT.x) / FOCAL_LEN_X_PX;
    double iris_y_mm = iris_z_mm * (ellipse.center.y - PRIN_POINT.y) / FOCAL_LEN_Y_PX;

	Vector3d limbus_center(iris_x_mm, iris_y_mm, iris_z_mm);

	double psi = CV_PI / 180.0 * (ellipse.angle+90);    // z-axis rotation (radians)
    double tht = acos(min_axis_px / maj_axis_px);       // y-axis rotation (radians)

	if (limbus_switch) tht = -tht;                      // ambiguous acos, so sometimes switch limbus

    // Get limbus normal for chosen theta
    Vector3d limb_normal(sin(tht) * cos(psi), -sin(tht) * sin(psi), -cos(tht));

	// Now correct for weak perspective by modifying angle by offset between camera axis and limbus
    double x_correction = -atan2(iris_y_mm, iris_z_mm);
    double y_correction = -atan2(iris_x_mm, iris_z_mm);
	AngleAxisd rot1(y_correction, Vector3d(0,-1,0));
	AngleAxisd rot2(x_correction, Vector3d(1,0,0));
	limb_normal = rot1 * limb_normal;
	limb_normal = rot2 * limb_normal;

	limbus_to_return.push_back(limbus_center);
	limbus_to_return.push_back(limb_normal);
	return limbus_to_return;
}

// returns intersection with z-plane of optical axis vector (mm)
Point2d get_gaze_point_mm(Vector3d limb_center, Vector3d limb_normal){

    // ray/plane intersection
    double t = -limb_center.z() / limb_normal.z();
    return Point2d(limb_center.x() + limb_normal.x() * t, limb_center.y() + limb_normal.y() * t);
}

Point2d get_gaze_pt_mm(RotatedRect& ellipse){

	// get two possible limbus centres and normals because of ambiguous trig
	vector<Vector3d> limbus_a = ellipse_to_limbus(ellipse, true);
	vector<Vector3d> limbus_b = ellipse_to_limbus(ellipse, false);

	// calculate gaze points for each possible limbus
	Point2d gp_mm_a = get_gaze_point_mm(limbus_a[0], limbus_a[1]);
	Point2d gp_mm_b = get_gaze_point_mm(limbus_b[0], limbus_b[1]);

	// calculate distance from centre of screen for each possible gaze point
	int dist_a = std::abs(gp_mm_a.x) + std::abs(gp_mm_a.y);
	int dist_b = std::abs(gp_mm_b.x) + std::abs(gp_mm_b.y);

	// return gaze point closest to screen centre
	return (dist_a < dist_b) ? gp_mm_a : gp_mm_b;
}

const Size SCREEN_SIZE_MM(236, 134);
const Size SCREEN_SIZE_PX(1920, 1080);		// screen size in pixels
const Point2i CAMERA_OFFSET_MM(120, 140);	// vector from top left of screen to camera

Point2i convert_gaze_pt_mm_to_px(Point2d gaze_pt_mm){

	int gp_px_x = (gaze_pt_mm.x + CAMERA_OFFSET_MM.x) / SCREEN_SIZE_MM.width * SCREEN_SIZE_PX.width;
    int gp_px_y = (gaze_pt_mm.y + CAMERA_OFFSET_MM.y) / SCREEN_SIZE_MM.height * SCREEN_SIZE_PX.height;

    return Point2i(gp_px_x, gp_px_y);
}

float scale = 720 / float(SCREEN_SIZE_PX.height);

// draws the gaze-points on-screen as circles and crosses
void show_gaze(Mat& img, vector<Point2i> gaze_pt_raw_s, vector<Scalar> colors_raw, Point2i gaze_pt_smoothed, Scalar color_smoothed){

	Mat screen(SCREEN_SIZE_PX.height, SCREEN_SIZE_PX.width, CV_8UC3);
	screen.setTo(YELLOW);

	// draw ra
	for (int i=0; i<gaze_pt_raw_s.size(); i++)
		circle(img, gaze_pt_raw_s[i] * scale, 10, colors_raw[i], -1);

	circle(img, gaze_pt_smoothed * scale, 20, color_smoothed, -1);
}


#include "distribution.h"
#include "reference.h"
using namespace std;
using namespace Rcpp ;
#include <boost/algorithm/string.hpp>

// [[Rcpp::depends(RcppEigen)]]
ReferenceF::ReferenceF(void) {
  distribution dist;
}

Eigen::VectorXd ReferenceF::inverse_logistic(const Eigen::VectorXd& eta) const
{
  Eigen::VectorXd pi( eta.size() );
  double norm1 = 1.;
  for(size_t j=0; j<eta.size(); ++j)
  {
    pi[j] = cdf_logit( eta(j) ) / ( 1-
      std::max(1e-10, std::min(1-1e-6,cdf_logit( eta(j) )))
    );

    norm1 += pi[j];
  }
  return (pi/norm1);
}

Eigen::VectorXd ReferenceF::inverse_normal(const Eigen::VectorXd& eta) const
{
  Eigen::VectorXd pi( eta.size() );
  double norm1 = 1.;
  for(size_t j=0; j<eta.size(); ++j)
  {
    pi[j] = cdf_normal( eta(j) ) / ( 1-
      std::max(1e-10, std::min(1-1e-6,cdf_normal( eta(j) )))
    );
    norm1 += pi[j];

  }
  return (pi/norm1);
}

Eigen::MatrixXd ReferenceF::inverse_derivative_logistic(const Eigen::VectorXd& eta2 ) const
{
  Eigen::VectorXd pi1 = ReferenceF::inverse_logistic(eta2);
  Eigen::MatrixXd D1 = Eigen::MatrixXd::Zero(pi1.rows(),pi1.rows());
  for(int j=0; j<eta2.rows(); ++j)
    // { D1(j,j) = pdf_logit( eta2(j) ) /
    //   (Logistic::cdf_logit(eta2(j)) * (1-Logistic::cdf_logit(eta2(j))));
    // }

  { D1(j,j) = pdf_logit( eta2(j) ) /
    ( std::max(1e-10, std::min(1-1e-6,cdf_logit(eta2(j)))) *
      std::max(1e-10, std::min(1-1e-6, 1-cdf_logit(eta2(j)))) ); }

  return D1 * ( Eigen::MatrixXd(pi1.asDiagonal()) - pi1 * pi1.transpose().eval() );
}

Eigen::MatrixXd ReferenceF::inverse_derivative_normal(const Eigen::VectorXd& eta) const
{
  Eigen::VectorXd pi = ReferenceF::inverse_normal(eta);
  Eigen::MatrixXd D = Eigen::MatrixXd::Zero(pi.rows(),pi.rows());
  for(size_t j=0; j<pi.rows(); ++j)
  { D(j,j) = pdf_normal( eta(j) ) /
    ( std::max(1e-10, std::min(1-1e-6,cdf_normal(eta(j)))) *
      std::max(1e-10, std::min(1-1e-6, 1-cdf_normal(eta(j)))) ); }
  return D * ( Eigen::MatrixXd(pi.asDiagonal()) - pi * pi.transpose().eval() );
}

Eigen::VectorXd ReferenceF::inverse_cauchit(const Eigen::VectorXd& eta) const
{
  Eigen::VectorXd pi( eta.size() );
  double norm1 = 1.;
  for(size_t j=0; j<eta.size(); ++j)
  {
    pi[j] = Cauchit::cdf_cauchit( eta(j) ) / ( 1-Cauchit::cdf_cauchit( eta(j) ) );
    norm1 += pi[j];
  }
  return (pi/norm1);
}

Eigen::MatrixXd ReferenceF::inverse_derivative_cauchit(const Eigen::VectorXd& eta2) const
{
  Eigen::VectorXd pi1 = ReferenceF::inverse_cauchit(eta2);
  Eigen::MatrixXd D1 = Eigen::MatrixXd::Zero(pi1.rows(),pi1.rows());
  for(int j=0; j<eta2.rows(); ++j)
  { D1(j,j) = pdf_cauchit( eta2(j) ) /
    (Cauchit::cdf_cauchit(eta2(j)) * (1-Cauchit::cdf_cauchit(eta2(j))));
  }
  return D1 * ( Eigen::MatrixXd(pi1.asDiagonal()) - pi1 * pi1.transpose().eval() );
}

Eigen::VectorXd ReferenceF::inverse_student(const Eigen::VectorXd& eta, const double& freedom_degrees) const
{
  Eigen::VectorXd pi( eta.size() );
  double norm1 = 1.;
  for(size_t j=0; j<eta.size(); ++j)
  {
    double num = Student::cdf_student(eta(j),freedom_degrees);
    double den = std::max(1e-10, std::min(1-1e-6, 1 - Student::cdf_student(eta(j),freedom_degrees)));
    pi[j] = (num / den);
    norm1 += pi[j];
  }
  return (pi/norm1);
}

Eigen::MatrixXd ReferenceF::inverse_derivative_student(const Eigen::VectorXd& eta2, const double& freedom_degrees) const
{
  Eigen::VectorXd pi1 = ReferenceF::inverse_student(eta2, freedom_degrees);
  Eigen::MatrixXd D1 = Eigen::MatrixXd::Zero(pi1.rows(),pi1.rows());
  for(int j=0; j<eta2.rows(); ++j)
  {
    double num = Student::pdf_student( eta2(j) , freedom_degrees);
    double den1 = Student::cdf_student(eta2(j), freedom_degrees) ;
    double den2 = 1-Student::cdf_student(eta2(j),freedom_degrees) ;
    D1(j,j) = (num / std::max(1e-10, std::min(1-1e-6, (den1 * den2)) ));
  }
  Eigen::MatrixXd D3 = pi1 * (pi1.transpose());
  Eigen::MatrixXd D2 = Eigen::MatrixXd(pi1.asDiagonal());
  Eigen::MatrixXd FINAL = D1 * ( D2 - D3 );
  return FINAL;
}

distribution dist1;

// [[Rcpp::export(".GLMref")]]
List GLMref(Formula formula,
            CharacterVector categories_order,
            CharacterVector proportional_effects,
            DataFrame data,
            std::string distribution,
            double freedom_degrees){

  const int N = data.nrows() ; // Number of observations

  List Full_M = dist1.All_pre_data_or(formula, data,
                                      categories_order, proportional_effects);

  Eigen::MatrixXd Y_init = Full_M["Response_EXT"];
  Eigen::MatrixXd X_EXT = Full_M["Design_Matrix"];
  CharacterVector levs1 = Full_M["Levels"];
  CharacterVector explanatory_complete = Full_M["Complete_effects"];
  int N_cats = Full_M["N_cats"];

  int P_c = explanatory_complete.length();
  int P_p = 0;
  if(proportional_effects[0] != "NA"){P_p = proportional_effects.length();}
  int P =  P_c +  P_p ; // Number of explanatory variables without intercept

  int Q = Y_init.cols();
  int K = Q + 1;
  // // // Beta initialization with zeros
  Eigen::MatrixXd BETA;
  BETA = Eigen::MatrixXd::Zero(X_EXT.cols(),1);
  //
  int iteration = 0;
  // double check_tutz = 1.0;
  double Stop_criteria = 1.0;
  Eigen::MatrixXd X_M_i ;
  Eigen::VectorXd Y_M_i ;
  Eigen::VectorXd eta ;
  Eigen::VectorXd pi ;
  Eigen::MatrixXd D ;
  Eigen::MatrixXd Cov_i ;
  Eigen::MatrixXd W_in ;
  Eigen::MatrixXd Score_i_2 ;
  Eigen::MatrixXd F_i_2 ;
  Eigen::VectorXd LogLikIter;
  LogLikIter = Eigen::MatrixXd::Zero(1,1) ;
  Eigen::MatrixXd var_beta;
  Eigen::VectorXd Std_Error;
  double LogLik;
  Eigen::MatrixXd pi_ma(N, K);
  Eigen::MatrixXd F_i_final = Eigen::MatrixXd::Zero(BETA.rows(), BETA.rows());

  // for (int iteration=1; iteration < 18; iteration++){
  // while (check_tutz > 0.0001){
  double epsilon = 0.0001 ;
  while (Stop_criteria >( epsilon / N) & iteration < 26){

    Eigen::MatrixXd Score_i = Eigen::MatrixXd::Zero(BETA.rows(),1);
    Eigen::MatrixXd F_i = Eigen::MatrixXd::Zero(BETA.rows(), BETA.rows());
    LogLik = 0.;

    // Loop by subject
    for (int i=0; i < N; i++){
      // Block of size (p,q), starting at (i,j): matrix.block(i,j,p,q);
      X_M_i = X_EXT.block(i*Q , 0 , Q , X_EXT.cols());
      Y_M_i = Y_init.row(i);
      eta = X_M_i * BETA;

      ReferenceF ref;

      // Vector pi depends on selected distribution
      if(distribution == "logistic"){
        pi = ref.inverse_logistic(eta);
        D = ref.inverse_derivative_logistic(eta);
      }else if(distribution == "normal"){
        pi = ref.inverse_normal(eta);
        D = ref.inverse_derivative_normal(eta);
      }else if(distribution == "cauchit"){
        pi = ref.inverse_cauchit(eta);
        D = ref.inverse_derivative_cauchit(eta);
      }else if(distribution == "student"){
        pi = ref.inverse_student(eta, freedom_degrees);
        D = ref.inverse_derivative_student(eta, freedom_degrees);
      }

      Cov_i = Eigen::MatrixXd(pi.asDiagonal()) - (pi*pi.transpose());
      // Rcout << Cov_i.determinant() << std::endl;
      W_in = D * Cov_i.inverse();
      Score_i_2 = X_M_i.transpose() * W_in * (Y_M_i - pi);
      Score_i = Score_i + Score_i_2;
      F_i_2 = X_M_i.transpose() * (W_in) * (D.transpose() * X_M_i);
      F_i = F_i + F_i_2;
      LogLik = LogLik + (Y_M_i.transpose().eval()*Eigen::VectorXd(pi.array().log())) + ( (1 - Y_M_i.sum()) * std::log(1 - pi.sum()) );

      pi_ma.row(i) = pi.transpose();

    }

    Eigen::VectorXd Ones1 = Eigen::VectorXd::Ones(pi_ma.rows());
    pi_ma.col(Q) = Ones1 - pi_ma.rowwise().sum() ;

    // To stop when LogLik is smaller than the previous
    if(iteration>1){
      if (LogLikIter[iteration] > LogLik)
        break;
      // iteration = 25;
    }

    // To stop when LogLik is smaller than the previous
    // if(iteration>1){
    // if (iteration == 25) {  break; }

    // }

    LogLikIter.conservativeResize(iteration+2, 1);
    LogLikIter(iteration+1) = LogLik;
    Stop_criteria = (abs(LogLikIter(iteration+1) - LogLikIter(iteration))) / (epsilon + (abs(LogLikIter(iteration+1)))) ;
    Eigen::VectorXd beta_old = BETA;

    if (F_i.determinant() < 0.000000000000000000001) {
      cout << "F_i.determinant() = 0" << endl;
      Rcpp::stop("F_i.determinant() = 0 \n Memory allocation failed!\n");
    }

    BETA = BETA + (F_i.inverse() * Score_i);
    // check_tutz = ((BETA - beta_old).norm())/(beta_old.norm()+check_tutz);
    iteration = iteration + 1;

    // if (iteration == 30) {
    //   cout << "Max iter" << endl;
    //   Rcpp::stop("Max iter");
    // }

    F_i_final = F_i;
    // Rcout << "BETA" << std::endl;
    // Rcout << BETA << std::endl;
    // Rcout << "LogLik" << std::endl;
    // Rcout << LogLik << std::endl;
  }

  // var_beta = (((X_EXT.transpose() * F_i_final) * X_EXT).inverse());
  var_beta = F_i_final.inverse();
  Std_Error = var_beta.diagonal();
  Std_Error = Std_Error.array().sqrt() ;

  std::vector<std::string> text=as<std::vector<std::string>>(explanatory_complete);
  std::vector<std::string> level_text=as<std::vector<std::string>>(levs1);
  StringVector names(Q*P_c + P_p);

  if(P_c > 0){
    for(int var = 0 ; var < explanatory_complete.size() ; var++){
      for(int cat = 0 ; cat < Q ; cat++){
        names[(Q*var) + cat] = dist1.concatenate(text[var], level_text[cat]);
      }
    }
  }
  if(P_p > 0){
    for(int var_p = 0 ; var_p < proportional_effects.size() ; var_p++){
      names[(Q*P_c) + var_p] = proportional_effects[var_p];
    }
  }

  // TO NAMED THE RESULT BETAS
  NumericMatrix coef = wrap(BETA);
  rownames(coef) = names;

  // AIC
  double AIC = (-2*LogLik) + (2 *coef.length());

  // AIC
  double BIC = (-2*LogLik) + (coef.length() * log(N) );

  int df = (N*Q) - coef.length();

  Eigen::MatrixXd predicted = X_EXT * BETA;

  Eigen::VectorXd Ones2 = Eigen::VectorXd::Ones(Y_init.rows());
  Eigen::VectorXd vex1 = (Y_init.rowwise().sum()) ;
  Y_init.conservativeResize( Y_init.rows(), K);
  Y_init.col(Q) = (vex1 - Ones2).array().abs() ;
  Eigen::MatrixXd residuals = Y_init - pi_ma;
  Eigen::VectorXd pi_ma_vec(Eigen::Map<Eigen::VectorXd>(pi_ma.data(), pi_ma.cols()*pi_ma.rows()));
  Eigen::VectorXd Y_init_vec(Eigen::Map<Eigen::VectorXd>(Y_init.data(), Y_init.cols()*Y_init.rows()));
  Eigen::VectorXd div_arr = Y_init_vec.array() / pi_ma_vec.array();
  Eigen::VectorXd dev_r(Y_init.rows());
  int el_1 = 0;
  for (int element = 0 ; element < div_arr.size() ;  element++){
    if (div_arr[element] != 0){
      dev_r[el_1] = div_arr[element];
      el_1 = el_1 +1 ;
    }
  }
  Eigen::ArrayXd dev_log = dev_r.array().log();
  double deviance = dev_log.sum();
  deviance = -2*deviance;

  return List::create(
    Named("coefficients") = coef,
    Named("iteration") = iteration,
    // Named("AIC") = AIC,
    // Named("BIC") = BIC,
    Named("freedom_degrees") = freedom_degrees,
    Named("levs1") = levs1,
    Named("stderr") = Std_Error,
    Rcpp::Named("df") = df,
    Rcpp::Named("predicted") = predicted,
    Rcpp::Named("fitted") = pi_ma,
    Rcpp::Named("pi_ma_vec") = pi_ma_vec,
    Rcpp::Named("Y_init_vec") = Y_init_vec,
    Rcpp::Named("dev_log") = dev_log,
    Rcpp::Named("deviance") = deviance,
    Rcpp::Named("residuals") = residuals,
    Named("Log-likelihood") = LogLik,
    // Named("freedom_degrees") = freedom_degrees,
    // Named("Y_init") = Y_init,
    Named("LogLikIter") = LogLikIter,
    Named("formula") = formula,
    Named("categories_order") = categories_order,
    Named("proportional_effects") = proportional_effects,
    Named("N_cats") = N_cats,
    Named("distribution") = distribution
  );
}

// [[Rcpp::export(".Predict_Response")]]
List Predict_Response(List model_object,
                      DataFrame NEWDATA){
  Environment base_env("package:base");
  Function my_rowSums = base_env["rowSums"];

  int N_cats = model_object["N_cats"];
  Eigen::MatrixXd coef = model_object["coefficients"];

  List NewDataList = dist1.All_pre_data_NEWDATA(model_object["formula"],
                                                NEWDATA,
                                                model_object["categories_order"],
                                                            model_object["proportional_effects"],
                                                                        N_cats

  );

  Eigen::MatrixXd Design_Matrix = NewDataList["Design_Matrix"];
  Eigen::MatrixXd predicted_eta;

  String distribution = model_object["distribution"];
  double freedom_degrees = model_object["freedom_degrees"];

  ReferenceF ref;
  Eigen::VectorXd pi;
  int N = NEWDATA.rows();
  Eigen::MatrixXd X_M_i;

  Eigen::MatrixXd pi_total = Eigen::MatrixXd::Zero(N,N_cats-1);

  for (int i=0; i < N; i++){

    X_M_i = Design_Matrix.block(i*(N_cats-1) , 0 , N_cats-1 , Design_Matrix.cols());

    predicted_eta = X_M_i * coef;

    if(distribution == "logistic"){
      pi = ref.inverse_logistic(predicted_eta);
    }else if(distribution == "normal"){
      pi = ref.inverse_normal(predicted_eta);
    }else if(distribution == "cauchit"){
      pi = ref.inverse_cauchit(predicted_eta);
    }else if(distribution == "student"){
      pi = ref.inverse_student(predicted_eta, freedom_degrees);
    }
    pi_total.row(i) = pi;

  }

  NumericVector cum_prob = my_rowSums(pi_total);
  Eigen::Map<Eigen::VectorXd> cum_prob1 = as<Eigen::Map<Eigen::VectorXd> >(cum_prob);
  Eigen::VectorXd Ones1 = Eigen::VectorXd::Ones(pi_total.rows());

  pi_total.conservativeResize(pi_total.rows() , N_cats);
  pi_total.col(N_cats-1) = Ones1 - cum_prob1;

  return List::create(
    Named("Design_Matrix") = Design_Matrix,
    Named("Eta") = predicted_eta,
    // Named("cum_prob") = cum_prob,
    Named("pi_total") = pi_total
  );

}

// [[Rcpp::export(".Discrete_CM")]]
List Discrete_CM(Formula formula,
                 String case_id,
                 String alternatives,
                 SEXP reference,
                 CharacterVector alternative_specific,
                 DataFrame data,
                 std::string distribution,
                 double freedom_degrees
){

  List Full_M = dist1.select_data_nested(formula,
                                         case_id,
                                         alternatives,
                                         reference,
                                         alternative_specific,
                                         data
  );

  Eigen::MatrixXd Y_init = Full_M["Response_M"];
  Eigen::MatrixXd X_EXT = Full_M["Design_Matrix"];

  int Q = Y_init.cols();
  int K = Q + 1;
  int N = K * Y_init.rows();

  Eigen::MatrixXd BETA;
  BETA = Eigen::MatrixXd::Zero(X_EXT.cols(),1); // Beta initialization with zeros
  int iteration = 0;
  double Stop_criteria = 1.0;
  Eigen::MatrixXd X_M_i ;
  Eigen::VectorXd Y_M_i ;
  Eigen::VectorXd eta ;
  Eigen::VectorXd pi ;
  Eigen::MatrixXd D ;
  Eigen::MatrixXd Cov_i ;
  Eigen::MatrixXd W_in ;
  Eigen::MatrixXd Score_i_2 ;
  Eigen::MatrixXd F_i_2 ;
  Eigen::VectorXd LogLikIter;
  LogLikIter = Eigen::MatrixXd::Zero(1,1) ;
  double LogLik;

  Eigen::MatrixXd F_i_final = Eigen::MatrixXd::Zero(BETA.rows(), BETA.rows());
  Eigen::MatrixXd var_beta;
  Eigen::VectorXd Std_Error;

  double epsilon = 0.0001 ;
  // for (int iteration=1; iteration < 18; iteration++){
  while (Stop_criteria >( epsilon / N)){
    Eigen::MatrixXd Score_i = Eigen::MatrixXd::Zero(BETA.rows(),1);
    Eigen::MatrixXd F_i = Eigen::MatrixXd::Zero(BETA.rows(), BETA.rows());
    LogLik = 0.;
    ReferenceF ref;

    for (int i=0; i < N/K; i++){
      X_M_i = X_EXT.block(i*Q , 0 , Q , X_EXT.cols());
      Y_M_i = Y_init.row(i);
      eta = X_M_i * BETA;

      if(distribution == "logistic"){
        pi = ref.inverse_logistic(eta);
        D = ref.inverse_derivative_logistic(eta);
      }else if(distribution == "normal"){
        pi = ref.inverse_normal(eta);
        D = ref.inverse_derivative_normal(eta);
      }else if(distribution == "cauchit"){
        pi = ref.inverse_cauchit(eta);
        D = ref.inverse_derivative_cauchit(eta);
      }else if(distribution == "student"){
        pi = ref.inverse_student(eta, freedom_degrees);
        D = ref.inverse_derivative_student(eta, freedom_degrees);
      }
      Cov_i = Eigen::MatrixXd(pi.asDiagonal()) - (pi*pi.transpose());
      W_in = D * Cov_i.inverse();
      Score_i_2 = X_M_i.transpose() * W_in * (Y_M_i - pi);
      Score_i = Score_i + Score_i_2;
      F_i_2 = X_M_i.transpose() * (W_in) * (D.transpose() * X_M_i);
      F_i = F_i + F_i_2;
      LogLik = LogLik + (Y_M_i.transpose().eval()*Eigen::VectorXd(pi.array().log())) + ( (1 - Y_M_i.sum()) * std::log(1 - pi.sum()) );
    }

    // To stop when LogLik is smaller than the previous
    if(iteration>1){
      if (LogLikIter[iteration] > LogLik)
        break;
    }

    LogLikIter.conservativeResize( LogLikIter.rows() +1 , 1);
    LogLikIter(LogLikIter.rows() - 1) = LogLik;
    Stop_criteria = (abs(LogLikIter(iteration+1) - LogLikIter(iteration))) / (epsilon + (abs(LogLikIter(iteration+1)))) ;
    Eigen::VectorXd beta_old = BETA;
    BETA = BETA + (F_i.inverse() * Score_i);

    iteration = iteration + 1;
    // Rcout << "BETA" << std::endl;
    // Rcout << BETA << std::endl;

    // Rcout << "iteration" << std::endl;
    // Rcout << iteration << std::endl;
    // Rcout << "LogLik" << std::endl;
    // Rcout << LogLik << std::endl;
    F_i_final = F_i;

    // Rcout << "LogLikIter" << std::endl;
    // Rcout << LogLikIter << std::endl;

  }

  var_beta = F_i_final.inverse();
  Std_Error = var_beta.diagonal();
  Std_Error = Std_Error.array().sqrt() ;

  // Eigen::MatrixXd X_M_i_1 = X_EXT.block(0*Q , 0 , Q , X_EXT.cols());
  // Eigen::VectorXd Y_M_i_1 = Y_init.row(0);

  NumericMatrix BETA_2 = wrap(BETA);

  return List::create(
    Named("Nb. iterations") = iteration-1 ,
    Named("coefficients") = BETA_2,
    Named("Log-likelihood") = LogLikIter(LogLikIter.rows() - 1),
    Named("LogLikIter") =  LogLikIter,
    Named("stderr") =  Std_Error
  );

}

RCPP_MODULE(referencemodule){
  Rcpp::function("GLMref", &GLMref,
                 List::create(_["formula"] = R_NaN,
                              _["categories_order"] = CharacterVector::create( "A", NA_STRING),
                              _["proportional_effects"] = CharacterVector::create(NA_STRING),
                              _["data"] = NumericVector::create( 1, NA_REAL, R_NaN, R_PosInf, R_NegInf),
                              _["distribution"] = "a",
                              _["freedom_degrees"] = 1.0),
                              "Reference model");

  Rcpp::function("Discrete_CM", &Discrete_CM,
                 List::create(_["formula"] = R_NaN,
                              _["case_id"] = "a",
                              _["alternatives"] = "a",
                              _["reference"] = R_NaN,
                              _["alternative_specific"] = CharacterVector::create( NA_STRING),
                              _["data"] = NumericVector::create( 1, NA_REAL, R_NaN, R_PosInf, R_NegInf),
                              _["distribution"] = "a",
                              _["freedom_degrees"] = 1.0),
                              "Discrete Choice Model");

  Rcpp::function("Predict_Response", &Predict_Response,
                 List::create(_["model_object"] = R_NaN,
                              _["data"] = NumericVector::create( 1, NA_REAL, R_NaN, R_PosInf, R_NegInf)
                 ),
                 "Predict_Response Choice Model");

  Rcpp::class_<ReferenceF>("ReferenceF")
    .constructor()
    .method( "inverse_logistic", &ReferenceF::inverse_logistic )
  ;
}


// Author: Daisuke Kanaizumi
// Affiliation: Department of Applied Mathematics, Waseda University

// verification program for q-Bessel functions
// April 12th, 2018

#ifndef QBESSEL_HPP
#define QBESSEL_HPP

#include <kv/interval.hpp>
#include <kv/rdouble.hpp>
#include <kv/constants.hpp>
#include <kv/complex.hpp>
#include <kv/convert.hpp>
#include <kv/defint.hpp>
#include <kv/Heine.hpp>
#include <kv/Pochhammer.hpp>
#include <kv/QHypergeometric.hpp>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublas/io.hpp>
#include <limits>
namespace ub = boost::numeric::ublas;
namespace kv{
template <class T> interval<T>Jackson1(const interval<T>& z,const interval<T>& nu,const interval<T>& q){
// verification program for Jackson`s 1st q-Bessel function
interval<T>res,a,b,c,series;
int j,K;
T rad;
if(abs(z)>=2){
throw std::domain_error("Jackson`s 1st q-Bessel function is not defined");
}
if(abs(q)>=1){
throw std::domain_error("absolute value of q must be under 1");
}
 if(z<0){
   throw std::domain_error("z must be positive");
 }
 if (pow(q,nu)<1){
j=1;
K=500;
a=1.;
b=1.;
c=1.;
// Leibniz criterion
 for (int k=1; k<=K; k++){
    j = -1*j;
    b=b*(1-pow(q,k));
    c=c*(1-pow(q,k+nu));
    a = a+j*pow(z/2,2*k)/(b*c);
  }
b=b*(1-pow(q,K+1));
c=c*(1-pow(q,K+nu+1));

rad=abs(pow(z/2,2*K+2)/(b*c)).upper();
series=a+rad*interval<T>(-1.,1.);
res=pow(z/2,nu)*series/Karpelevich(interval<T>(pow(q,nu+1)),interval<T>(q));
}
 else{
res=pow(z/2,nu)*infinite_qPochhammer(interval<T>(pow(q,nu+1)),interval<T>(q))*
  Heine(interval<T>(0),interval<T>(0),interval<T>(pow(q,nu+1)),interval<T>(q),interval<T>(-z*z/4))/Euler(interval<T>(q));
}

 return res;
}
template <class T> complex<interval<T> >Jackson1(const complex<interval<T> >& z,const complex<interval<T> >& nu,const interval<T>& q){
  complex<interval<T> >res,i;

if(abs(q)>=1){
throw std::domain_error("absolute value of q must be under 1");
}
 if(abs(z).upper()>=2){
throw std::domain_error("Jackson`s 1st q-Bessel function is not defined");
}

res=pow(z/2,nu)*infinite_qPochhammer(complex<interval<T> >(pow(q,nu+1)),interval<T>(q))*
  Heine(complex<interval<T> >(0),complex<interval<T> >(0),complex<interval<T> >(pow(q,nu+1)),interval<T>(q),complex<interval<T> >(-z*z/4))/Euler(interval<T>(q));

 return res;
}

template <class T> complex<interval<T> >modified_qBesselI1(const complex<interval<T> >& z,const complex<interval<T> >& nu,const interval<T>& q){
  // verification program for 1st modified q-Bessel I1
  complex<interval<T> >res,i;
  if(abs(q)>=1){
    throw std::domain_error("absolute value of q must be under 1");
  }
  if(abs(z).upper()>=2){
    throw std::domain_error("1st modified q-Bessel function is not defined");
  }
  interval<T>pi;
  i=complex<interval<T> >::i();
  pi=constants<interval<T> >::pi();
  res=exp(-i*nu*pi/2)*Jackson1(complex<interval<T> >(i*z),complex<interval<T> >(nu),interval<T> (q));
  return res;
}

  template <class T> complex<interval<T> >modified_qBesselK1(const complex<interval<T> >& z,const complex<interval<T> >& nu,const interval<T>& q){
    // verification program for 1st modified q-Bessel K1, nu should not be an integer
    complex<interval<T> >res;
    if(abs(q)>=1){
      throw std::domain_error("absolute value of q must be under 1");
    }
    if(abs(z).upper()>=2){
      throw std::domain_error("1st modified q-Bessel function is not defined");
    }
    interval<T>pi;
    pi=constants<interval<T> >::pi();
    res=pi*0.5/sin(pi*nu)*
      (modified_qBesselI1(complex<interval<T> > (z), complex<interval<T> > (-nu), interval<T> (q))-modified_qBesselI1(complex<interval<T> > (z), complex<interval<T> > (nu), interval<T> (q)));
    return res;
  }
  template <class T> interval<T>Jackson2(const interval<T>& z,const interval<T>& nu,const interval<T>& q){
    // verification program for Jackson`s 2nd q-Bessel function
  interval<T>res,a,b,c,series,pq;
int j,K;
T rad;
if(abs(q)>=1){
throw std::domain_error("absolute value of q must be under 1");
}

// if (pow(q,nu)<1 && z*z<4*q){
/*j=1;
K=500;
a=1.;
b=1.;
c=1.;
// Leibniz criterion
 for (int k=1; k<=K; k++){
    j = -1*j;
    b=b*(1-pow(q,k));
    c=c*(1-pow(q,k+nu));
    a = a+j*pow(q,k*(k-1))*pow(pow(q,nu+1)*z*z/4,k)/(b*c);
  }
  b=b*(1-pow(q,K+1));
  c=c*(1-pow(q,K+nu+1));

 rad=abs(pow(q,K*(K+1))*pow(pow(q,nu+1)*z*z/4,K+1)/(b*c)).upper();
 series=a+rad*interval<T>(-1.,1.);
 res=pow(z/2,nu)*series/Karpelevich(interval<T>(pow(q,nu+1)),interval<T>(q));
*/ //}
// else{
   /* implementation by original definition*/
// res=pow(z*0.5,nu)*infinite_qPochhammer(interval<T>(pow(q,nu+1)),interval<T>(q))*
//  _0phi_1(interval<T>(pow(q,nu+1)),interval<T>(q),interval<T>(-z*z*pow(q,nu+1)/4))/Euler(interval<T>(q));

 // alternative implementaion
 // reference
 // H. T Koelink, Hansen-Lommel orthogonality Relations for Jackson`s q-Bessel functions, formula 3.2
 // Journal of Mathematical Analysis and Applications 175, 425-437 (1993)
 pq=pow(q,nu+1);
 res=pow(z/2,nu)*_1phi_1(interval<T> (-z*z/4),interval<T> (0),interval<T>(q), interval<T> (pq))/Euler(interval<T>(q));
 // }

  return res;
}
template <class T> complex<interval<T> >Jackson2(const complex<interval<T> >& z,const complex<interval<T> >& nu,const interval<T>& q){
  complex<interval<T> >res,pq,i;
  i=complex<interval<T> >::i();
  if(abs(q)>=1){
    throw std::domain_error("absolute value of q must be under 1");
  }
  pq=pow(q,nu+1);
  /* implementation by original definition*/

  //  res=pow(z/2,nu)*infinite_qPochhammer(complex<interval<T> >(pq),interval<T>(q))*
  //  _0phi_1(complex<interval<T> >(pq),interval<T>(q),complex<interval<T> >(-z*z*pq/4))/Euler(interval<T>(q));

  // alternative implementaion
  // reference
  // H. T Koelink, Hansen-Lommel Orthogonality Relations for Jackson`s q-Bessel functions, formula 3.2
  // Journal of Mathematical Analysis and Applications 175, 425-437 (1993)
   try{
  res=pow(z/2,nu)*_1phi_1(complex<interval<T> >(-z*z/4),complex<interval<T> >(0),interval<T>(q), complex<interval<T> >(pq))/Euler(interval<T>(q));
  }
  // alternative implementaion
  // reference
  // Y. Chen , M. E. Ismail, K. A. Muttalib. Asymptotics of basic Bessel functions and q-Laguerre polynomials, Lemma 2
  // Journal of Computational and Applied Mathematics, 54(3), 263-272 (1994).
  catch(std::domain_error){
    ub::vector< complex<interval<T> > > a(3);
    ub::vector< complex<interval<T> > > b(2);
    ub::vector< complex<interval<T> > > c(2);
    a(0)=pow(q,(nu+0.5)*0.5);
    a(1)=-pow(q,(nu+0.5)*0.5);
    a(2)=0.;
    b(0)=-sqrt(q);
    b(1)=i*a(0)*z*0.5;
    c(0)=-sqrt(q);
    c(1)=-b(1);

    res=pow(z*0.5,nu)*infinite_qPochhammer(interval<T>(sqrt(q)),interval<T>(q))/Euler(interval<T>(q))*0.5
      *(infinite_qPochhammer(complex<interval<T> >(b(1)),interval<T>(sqrt(q)))
	*QHypergeom(ub::vector<complex<interval<T> > >(a),ub::vector<complex<interval<T> > >(b),interval<T>(sqrt(q)),complex<interval<T> >(sqrt(q),0))+
	infinite_qPochhammer(complex<interval<T> >(c(1)),interval<T>(sqrt(q)))
	*QHypergeom(ub::vector<complex<interval<T> > >(a),ub::vector<complex<interval<T> > >(c),interval<T>(sqrt(q)),complex<interval<T> >(sqrt(q),0)));
    	}

    if((abs(res)).upper()==std::numeric_limits<T>::infinity()){
    ub::vector< complex<interval<T> > > a(3);
    ub::vector< complex<interval<T> > > b(2);
    ub::vector< complex<interval<T> > > c(2);
    a(0)=pow(q,(nu+0.5)*0.5);
    a(1)=-pow(q,(nu+0.5)*0.5);
    a(2)=0.;
    b(0)=-sqrt(q);
    b(1)=i*a(0)*z*0.5;
    c(0)=-sqrt(q);
    c(1)=-b(1);

    res=pow(z*0.5,nu)*infinite_qPochhammer(interval<T>(sqrt(q)),interval<T>(q))/Euler(interval<T>(q))*0.5
      *(infinite_qPochhammer(complex<interval<T> >(b(1)),interval<T>(sqrt(q)))
	*QHypergeom(ub::vector<complex<interval<T> > >(a),ub::vector<complex<interval<T> > >(b),interval<T>(sqrt(q)),complex<interval<T> >(sqrt(q),0))+
	infinite_qPochhammer(complex<interval<T> >(c(1)),interval<T>(sqrt(q)))
	*QHypergeom(ub::vector<complex<interval<T> > >(a),ub::vector<complex<interval<T> > >(c),interval<T>(sqrt(q)),complex<interval<T> >(sqrt(q),0)));
     }
    //std::cout<<qPochhammer(complex<interval<T> >(pow(q,(nu+0.5)*0.5)),interval<T>(q),int(1000))
    //*qPochhammer(complex<interval<T> >(-pow(q,(nu+0.5)*0.5)),interval<T>(q),int(1000))<<std::endl;

    return res;
}
  template <class TT> struct qBesselintegral_nu_int_real {
    TT x, q;
    int nu,n; // Setting parameters
    qBesselintegral_nu_int_real(TT x, TT q, int nu,int n) : x(x),q(q),nu(nu),n(n) {}

    template <class T> T operator() (const T& t) {
      complex<T>  pro;
      T proreal;
      complex<T> i;
      pro=1.;
      i=complex<T>::i();
      for(int k=0;k<=nu-1;k++){
	pro=pro*(1-pow(T(q),k)*exp(2*i*t))*(1-pow(T(q),k)*exp(-2*i*t));
      }
      for(int j=0;j<=n-1;j++){
	pro=pro*(1+i*T(x)*pow(T(q),T(nu)/2+0.5+j)*exp(i*t)/2)*(1+i*T(x)*pow(T(q),T(nu)/2+0.5+j)*exp(-i*t)/2);
      }

      proreal=pro.real();
      return proreal;
    }
  };
  template <class TT> struct qBesselintegral_nu_int_imag {
    TT x, q;
    int nu,n; // Setting parameters
    qBesselintegral_nu_int_imag(TT x, TT q, int nu,int n) : x(x),q(q),nu(nu),n(n) {}

    template <class T> T operator() (const T& t) {
      complex<T>  pro;
      T proimag;
      complex<T> i;
      pro=1.;
      i=complex<T>::i();
      for(int k=0;k<=nu-1;k++){
	pro=pro*(1-pow(T(q),k)*exp(2*i*t))*(1-pow(T(q),k)*exp(-2*i*t));
      }
      for(int j=0;j<=n-1;j++){
	pro=pro*(1+i*T(x)*pow(T(q),T(nu)/2+0.5+j)*exp(i*t)/2)*(1+i*T(x)*pow(T(q),T(nu)/2+0.5+j)*exp(-i*t)/2);
      }

      proimag=pro.imag();
      return proimag;
    }
  };
  template <class T> complex<interval<T> >Jackson2_integral(const interval<T>& z,const int & nu,const interval<T>& q){
      // verification program for Jackson`s 2nd q-Bessel function
      // Integral representation is used

      // References
      // Rahman(1987), An Integral Representation and Some Transformation Properties of q-Bessel Functions, Journal of Mathematical Analysis and Applications 125
      // Zhang(2008), Plancherel-Rotach asymptotics for certain basic hypergeometric series, Advances in Mathematics 217, Lemma 1.1
    int n;
    n=100;
    if(nu<=0){
      throw std::domain_error("nu must be positive");
    }
    if(abs(z)*pow(q,nu/2+n+0.5)/2/(1-q)>=0.5){
      n=n+10;
    }
    complex<interval<T> >res,integral;
    interval<T>num,realint,imagint,pi;
    T numrad;
    numrad=(abs(z)*pow(q,nu/2+n+0.5)*2/(1-q)).upper();
    num=pow((1+numrad*interval<T>(-1.,1.)),2);
    pi=constants<interval<T> >::pi();
    realint=defint(qBesselintegral_nu_int_real<interval<T> >(z,q,nu,n),interval<T>(0.),interval<T>(pi),10,10);
    imagint=defint(qBesselintegral_nu_int_imag<interval<T> >(z,q,nu,n),interval<T>(0.),interval<T>(pi),10,10);
    integral=complex<interval<T> >(realint,imagint);

    res=num*integral*pow(z/2,nu)*infinite_qPochhammer(interval<T>(pow(q,2*nu)),interval<T>(q))/infinite_qPochhammer(interval<T>(pow(q,nu)),interval<T>(q))/(2*pi);

    return res;
  }
    template <class TT> struct qBesselintegral_nu_double_real {
      TT x,q,nu;
      int n; // Setting parameters
      qBesselintegral_nu_double_real(TT x, TT q, TT nu,int n) : x(x),q(q),nu(nu),n(n) {}

      template <class T> T operator() (const T& t) {
      complex<T>  pro;
      T proreal;
      complex<T> i;
      pro=1.;
      i=complex<T>::i();
      for(int k=0;k<=n-1;k++){
	pro=pro*(1-pow(T(q),k)*exp(2*i*t))*(1-pow(T(q),k)*exp(-2*i*t));
      }
      for(int j=0;j<=n-1;j++){
	pro=pro*(1+i*T(x)*pow(T(q),T(nu)/2+0.5+j)*exp(i*t)/2)*(1+i*T(x)*pow(T(q),T(nu)/2+0.5+j)*exp(-i*t)/2);
      }
      for(int l=0;l<=n-1;l++){
	pro=pro/(1-pow(T(q),l+nu)*exp(2*i*t))/(1-pow(T(q),l+nu)*exp(-2*i*t));
      }
      proreal=pro.real();
      return proreal;
      }
  };
  template <class TT> struct qBesselintegral_nu_double_imag {
    TT x,q,nu;
    int n; // Setting parameters
    qBesselintegral_nu_double_imag(TT x, TT q, TT nu,int n) : x(x),q(q),nu(nu),n(n) {}

    template <class T> T operator() (const T& t) {
      complex<T>  pro;
      T proimag;
      complex<T> i;
      pro=1.;
      i=complex<T>::i();
      for(int k=0;k<=n-1;k++){
	pro=pro*(1-pow(T(q),k)*exp(2*i*t))*(1-pow(T(q),k)*exp(-2*i*t));
      }
      for(int j=0;j<=n-1;j++){
	pro=pro*(1+i*T(x)*pow(T(q),T(nu)/2+0.5+j)*exp(i*t)/2)*(1+i*T(x)*pow(T(q),T(nu)/2+0.5+j)*exp(-i*t)/2);
      }
      for(int l=0;l<=n-1;l++){
	pro=pro/(1-pow(T(q),l+nu)*exp(2*i*t))/(1-pow(T(q),l+nu)*exp(-2*i*t));
      }

      proimag=pro.imag();
      return proimag;
    }
  };
  template <class T> complex<interval<T> >Jackson2_integral(const interval<T>& z,const interval<T> & nu,const interval<T>& q){
      // verification program for Jackson`s 2nd q-Bessel function
      // Integral representation is used

      // References
      // Rahman(1987), An Integral Representation and Some Transformation Properties of q-Bessel Functions, Journal of Mathematical Analysis and Applications 125
      // Zhang(2008), Plancherel-Rotach asymptotics for certain basic hypergeometric series, Advances in Mathematics 217, Lemma 1.1
    int n;
    n=100;
    if(nu<=0){
      throw std::domain_error("nu must be positive");
    }
    if(abs(z)*pow(q,nu/2+n+0.5)/2/(1-q)>=0.5){
      n=n+10;
    }
    if(pow(q,nu+n)/2/(1-q)>=0.5){
      n=n+10;
    }

    complex<interval<T> >res,integral;
    interval<T>num1,num2,denom,realint,imagint,pi;
    T numrad1,numrad2,denomrad;
    numrad1=(abs(z)*pow(q,nu/2+n+0.5)*2/(1-q)).upper();
    num1=pow((1+numrad1*interval<T>(-1.,1.)),2);
    numrad2=(pow(q,n)*2/(1-q)).upper();
    num2=pow((1+numrad2*interval<T>(-1.,1.)),2);
    denomrad=(pow(q,nu+n)*2/(1-q)).upper();
    denom=pow((1+denomrad*interval<T>(-1.,1.)),2);
    pi=constants<interval<T> >::pi();
    realint=defint(qBesselintegral_nu_double_real<interval<T> >(z,q,nu,n),interval<T>(0.),interval<T>(pi),10,10);
    imagint=defint(qBesselintegral_nu_double_imag<interval<T> >(z,q,nu,n),interval<T>(0.),interval<T>(pi),10,10);
    integral=complex<interval<T> >(realint,imagint);

    res=num1*num2*denom*integral*pow(z/2,nu)*infinite_qPochhammer(interval<T>(pow(q,2*nu)),interval<T>(q))/infinite_qPochhammer(interval<T>(pow(q,nu)),interval<T>(q))/(2*pi);

    return res;
  }
    template <class TT> struct qBesselintegral_z_complex_real {
      TT q,nu;
      complex<TT> x;
      int n; // Setting parameters
      qBesselintegral_z_complex_real(complex<TT> x, TT q, TT nu,int n) : x(x),q(q),nu(nu),n(n) {}

      template <class T> T operator() (const T& t) {
      complex<T>  pro;
      T proreal;
      complex<T> i;
      pro=1.;
      i=complex<T>::i();
      for(int k=0;k<=n-1;k++){
	pro=pro*(1-pow(T(q),k)*exp(2*i*t))*(1-pow(T(q),k)*exp(-2*i*t));
      }
      for(int j=0;j<=n-1;j++){
	pro=pro*(1+i*complex<T>(x)*pow(T(q),T(nu)/2+0.5+j)*exp(i*t)/2)*(1+i*complex<T>(x)*pow(T(q),T(nu)/2+0.5+j)*exp(-i*t)/2);
      }
      for(int l=0;l<=n-1;l++){
	pro=pro/(1-pow(T(q),l+T(nu))*exp(2*i*t))/(1-pow(T(q),l+T(nu))*exp(-2*i*t));
      }
      proreal=pro.real();
      return proreal;
      }
  };
  template <class TT> struct qBesselintegral_z_complex_imag {
    TT q,nu;
    complex<TT> x;
    int n; // Setting parameters
    qBesselintegral_z_complex_imag(complex<TT> x, TT q, TT nu,int n) : x(x),q(q),nu(nu),n(n) {}

    template <class T> T operator() (const T& t) {
      complex<T>  pro;
      T proimag;
      complex<T> i;
      pro=1.;
      i=complex<T>::i();
      for(int k=0;k<=n-1;k++){
	pro=pro*(1-pow(T(q),k)*exp(2*i*t))*(1-pow(T(q),k)*exp(-2*i*t));
      }
      for(int j=0;j<=n-1;j++){
	pro=pro*(1+i*complex<T>(x)*pow(T(q),T(nu)/2+0.5+j)*exp(i*t)/2)*(1+i*complex<T>(x)*pow(T(q),T(nu)/2+0.5+j)*exp(-i*t)/2);
      }
      for(int l=0;l<=n-1;l++){
	pro=pro/(1-pow(T(q),l+T(nu))*exp(2*i*t))/(1-pow(T(q),l+T(nu))*exp(-2*i*t));
      }

      proimag=pro.imag();
      return proimag;
    }
  };
  template <class T> complex<interval<T> >Jackson2_integral(const complex<interval<T> >& z,const interval<T> & nu,const interval<T>& q){
      // verification program for Jackson`s 2nd q-Bessel function
      // Integral representation is used

      // References
      // Rahman(1987), An Integral Representation and Some Transformation Properties of q-Bessel Functions, Journal of Mathematical Analysis and Applications 125
      // Zhang(2008), Plancherel-Rotach asymptotics for certain basic hypergeometric series, Advances in Mathematics 217, Lemma 1.1
    int n;
    n=100;
    if(nu<=0){
      throw std::domain_error("nu must be positive");
    }
    if(abs(z*pow(q,nu/2+n+0.5))/2/(1-q)>=0.5){
      n=n+10;
    }
    if(abs(pow(q,nu+n))/2/(1-q)>=0.5){
      n=n+10;
    }

    complex<interval<T> >res,integral,num1,denom;

    interval<T> num2;
    interval<T> pi,realint,imagint;
    T numrad1,numrad2,denomrad;
    numrad1=(abs(z*pow(q,nu/2+n+0.5))*2/(1-q)).upper();
    num1=pow(complex_nbd(complex<interval<T> >(1,0),numrad1),2);
    numrad2=(pow(q,n)*2/(1-q)).upper();
    num2=pow((1+numrad2*interval<T>(-1.,1.)),2);
    denomrad=(abs(pow(q,nu+n))*2/(1-q)).upper();
    denom=pow(complex_nbd(complex<interval<T> >(1,0),denomrad),2);
    pi=constants<interval<T> >::pi();
    realint=defint(qBesselintegral_z_complex_real<interval<T> >(z,q,nu,n),interval<T>(0.),interval<T>(pi),10,10);
    imagint=defint(qBesselintegral_z_complex_imag<interval<T> >(z,q,nu,n),interval<T>(0.),interval<T>(pi),10,10);
    integral=complex<interval<T> >(realint,imagint);

    res=num1*num2*denom*integral*pow(z/2,nu)*infinite_qPochhammer(complex<interval<T> >(pow(q,2*nu)),interval<T>(q))/infinite_qPochhammer(complex<interval<T> >(pow(q,nu)),interval<T>(q))/(2*pi);

    return res;
  }
  template <class T> interval<T> J2ratio(const interval<T> & z,const interval<T> & nu,const interval<T>& q){
    interval<T>res;
    res=Jackson2(interval<T>(z),interval<T>(nu),interval<T>(q))/Jackson2(interval<T>(z),interval<T>(nu-1),interval<T>(q));
    return res;
  }
  template <class T> complex<interval<T> >modified_qBesselI2(const complex<interval<T> >& z,const complex<interval<T> >& nu,const interval<T>& q){
    //verification program for 2nd modified q-Bessel function I2
    complex<interval<T> >res,i;
    if(abs(q)>=1){
      throw std::domain_error("absolute value of q must be under 1");
    }
    /*implementation by original definition
      interval<T>pi;
      i=complex<interval<T> >::i();
      pi=constants<interval<T> >::pi();
      res=exp(-i*nu*pi/2)*Jackson2(complex<interval<T> >(i*z),complex<interval<T> >(nu),interval<T> (q));
    */
    // alternative implementation
    // reference
    // Ismail, M. E., & Zhang, R. (2015). $ q $-Bessel Functions and Rogers-Ramanujan Type Identities.
    // arXiv preprint arXiv:1508.06861.
    res=pow(z/2,nu)/Euler(interval<T>(q))
      *_1phi_1(complex<interval<T> >(z*z/4),complex<interval<T> >(0.),interval<T>(q),complex<interval<T> >(pow(q,nu+1)));
    return res;
  }
 template <class T> interval<T> modified_qBesselI2(const interval<T> & z,const interval<T> & nu,const interval<T>& q){
    //verification program for 2nd modified q-Bessel function I2
    interval<T> res;
    if(abs(q)>=1){
      throw std::domain_error("absolute value of q must be under 1");
    }
    /*implementation by original definition
      interval<T>pi;
      i=complex<interval<T> >::i();
      pi=constants<interval<T> >::pi();
      res=exp(-i*nu*pi/2)*Jackson2(complex<interval<T> >(i*z),complex<interval<T> >(nu),interval<T> (q));
    */
    // alternative implementation
    // reference
    // Ismail, M. E., & Zhang, R. (2015). $ q $-Bessel Functions and Rogers-Ramanujan Type Identities.
    // arXiv preprint arXiv:1508.06861.
    res=pow(z/2,nu)/Euler(interval<T>(q))
      *_1phi_1(interval<T> (z*z/4),interval<T> (0.),interval<T>(q),interval<T> (pow(q,nu+1)));
    return res;
  }
  /*template <class T> interval<T> modified_qBesselI2_ae(const interval<T> & z,const interval<T> & nu,const interval<T>& q){
    //verification program for 2nd modified q-Bessel function I2, z>0
    // reference
    // Ismail, M. E., & Zhang, R. (2015). $ q $-Bessel Functions and Rogers-Ramanujan Type Identities.
    // arXiv preprint arXiv:1508.06861.
    interval<T> res;
    if(abs(q)>=1){
      throw std::domain_error("absolute value of q must be under 1");
    }
    res=pow(z/2.,nu)*infinite_qPochhammer(interval<T>(sqrt(q)),interval<T>(q))*0.5/Euler(interval<T>(q))
      *(infinite_qPochhammer(interval<T>(z*0.5*pow(q,(nu+0.5)*0.5)),interval<T>(sqrt(q)))+infinite_qPochhammer(interval<T>(-z*0.5*pow(q,(nu+0.5)*0.5)),interval<T>(sqrt(q))));
    return res;
    }*/
  template <class T> interval<T> Hahn_Exton(const interval<T> & z,const interval<T> & nu,const interval<T>& q){
    // verification program for Hahn-Exton q-Bessel function
    interval<T> res,pq;
    if(abs(q)>=1){
      throw std::domain_error("absolute value of q must be under 1");
    }
    pq=pow(q,nu+1);
    /* implementation by original definition*/
    /*    res=pow(z,nu)*infinite_qPochhammer(interval<T> (pq),interval<T>(q))*
	  _1phi_1(interval<T> (0),interval<T> (pq),interval<T>(q),interval<T> (z*z*q))/Euler(interval<T>(q));*/
       // alternative implementation
       // A. B. Olde Daalhuis, Asymptotic Expansions for q-Gamma, q-Exponential and q-Bessel Functions, formula 4.6
       // Journal of Mathematical Analysis and Applications 186, 896-913 (1994)

    	 res=pow(z,nu)*infinite_qPochhammer(interval<T> (z*z*q),interval<T>(q))*_1phi_1(interval<T> (0),interval<T> (z*z*q),interval<T>(q),interval<T> (pq))/Euler(interval<T> (q));
       return res;
  }
  template <class T> complex<interval<T> >Hahn_Exton(const complex<interval<T> >& z,const complex<interval<T> >& nu,const interval<T>& q){
    // verification program for Hahn-Exton q-Bessel function
    complex<interval<T> >res,pq;
    if(abs(q)>=1){
      throw std::domain_error("absolute value of q must be under 1");
    }
    pq=pow(q,nu+1);
    /* implementation by original definition*/
    /*  res=pow(z,nu)*infinite_qPochhammer(complex<interval<T> >(pq),interval<T>(q))*
       _1phi_1(complex<interval<T> >(0),complex<interval<T> >(pq),interval<T>(q),complex<interval<T> >(z*z*q))/Euler(interval<T>(q));
    */
    // alternative implementation
    // A. B. Olde Daalhuis, Asymptotic Expansions for q-Gamma, q-Exponential and q-Bessel Functions, formula 4.6
    // Journal of Mathematical Analysis and Applications 186, 896-913 (1994)
    res=pow(z,nu)*infinite_qPochhammer(complex<interval<T> >(z*z*q),interval<T>(q))*_1phi_1(complex<interval<T> >(0),complex<interval<T> >(z*z*q),interval<T>(q), complex<interval<T> >(pq))/Euler(interval<T> (q));
     return res;
  }
   template <class T> interval<T> HEratio(const interval<T> & z,const interval<T> & nu,const interval<T>& q){
    interval<T>res;
    res=Hahn_Exton(interval<T>(z),interval<T>(nu),interval<T>(q))/Hahn_Exton(interval<T>(z),interval<T>(nu-1),interval<T>(q));
    return res;
  }

  template <class T> complex<interval<T> >little(const complex<interval<T> >& z,const complex<interval<T> >& nu,const interval<T>& q){
    // verification program for little q-Bessel function
    // reference
    // Koornwinder and Swarttouw, On q-analogues of the Fourier and Hankel transforms, 1992
    // Bouzeffour, New Addition Formula for the Little q-Bessel Functions, arXiv, 2013
    complex<interval<T> >res,pq;
    if(abs(q)>=1){
      throw std::domain_error("absolute value of q must be under 1");
    }
    pq=pow(q,nu+1);
    res=pow(z,nu)*infinite_qPochhammer(complex<interval<T> >(pq),interval<T>(q))*
      _1phi_1(complex<interval<T> >(0),complex<interval<T> >(pq),interval<T>(q),complex<interval<T> >(z))/Euler(interval<T>(q));
    return res;
  }

}

#endif


#include <iostream>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/matrix_proxy.hpp>
#include <boost/numeric/ublas/io.hpp>
#include <boost/algorithm/minmax.hpp>
#include <vector>

#include "dbscan.h"

namespace clustering
{
	DBSCAN::ClusterData DBSCAN::gen_cluster_data( size_t features_num, size_t elements_num )
	{
		DBSCAN::ClusterData cl_d( elements_num, features_num );

		for (size_t i = 0; i < elements_num; ++i)
		{
			for (size_t j = 0; j < features_num; ++j)
			{
				cl_d(i, j) = (-1.0 + rand() * (2.0) / RAND_MAX);
			}
		}

		return cl_d;
	}

	DBSCAN::FeaturesWeights DBSCAN::std_weights( size_t s )
	{
		// num cols
		DBSCAN::FeaturesWeights ws( s );

		for (size_t i = 0; i < s; ++i)
		{
			ws(i) = 1.0;
		}

		return ws;
	}

	DBSCAN::DBSCAN()
	{

	}

	void DBSCAN::init(double eps, size_t min_elems/*, int num_threads*/)
	{
		m_eps = eps;
		m_min_elems = min_elems;
	}

	DBSCAN::DBSCAN(double eps, size_t min_elems/*, int num_threads*/)
	: m_eps( eps )
	, m_min_elems( min_elems )
	, m_dmin(0.0)
	, m_dmax(0.0)
	{
		reset();
	}

	DBSCAN::~DBSCAN()
	{

	}

	void DBSCAN::reset()
	{
		m_labels.clear();
	}

	void DBSCAN::prepare_labels( size_t s )
	{
		m_labels.resize(s);

		for( auto & l : m_labels)
		{
			l = -1;
		}
	}

	const DBSCAN::DistanceMatrix DBSCAN::calc_dist_matrix( const DBSCAN::ClusterData & C, const DBSCAN::FeaturesWeights & W )
	{
		DBSCAN::ClusterData cl_d = C;

		for (size_t i = 0; i < cl_d.size2(); ++i)
		{
			ublas::matrix_column<DBSCAN::ClusterData>col(cl_d, i);

			const auto r = minmax_element( col.begin(), col.end() );

			double data_min = *r.first;
			double data_range = *r.second - *r.first;

			if (data_range == 0.0) { data_range = 1.0; }

			const double scale = 1/data_range;
			const double min = -1.0*data_min*scale;

			col *= scale;
			col.plus_assign( ublas::scalar_vector< typename ublas::matrix_column<DBSCAN::ClusterData>::value_type >(col.size(), min) );
		}

		// rows x rows
		DBSCAN::DistanceMatrix d_m( cl_d.size1(), cl_d.size1() );
		ublas::vector<double> d_max( cl_d.size1() );
		ublas::vector<double> d_min( cl_d.size1() );

		for (size_t i = 0; i < cl_d.size1(); ++i)
		{
			for (size_t j = i; j < cl_d.size1(); ++j)
			{
				d_m(i, j) = 0.0;

				if (i != j)
				{
					ublas::matrix_row<DBSCAN::ClusterData> U (cl_d, i);
					ublas::matrix_row<DBSCAN::ClusterData> V (cl_d, j);

					int k = 0;
					for (const auto e : ( U-V ) )
					{
						d_m(i, j) += fabs(e)*W[k++];
					}

					d_m(j, i) = d_m(i, j);
				}
			}

			const auto cur_row = ublas::matrix_row<DBSCAN::DistanceMatrix>(d_m, i);
			const auto mm = minmax_element( cur_row.begin(), cur_row.end() );

			d_max(i) = *mm.second;
			d_min(i) = *mm.first;
		}

		m_dmin = *(min_element( d_min.begin(), d_min.end() ));
		m_dmax = *(max_element( d_max.begin(), d_max.end() ));

		m_eps = (m_dmax - m_dmin) * m_eps + m_dmin;

		return d_m;
	}

	DBSCAN::Neighbors DBSCAN::find_neighbors(const DBSCAN::DistanceMatrix & D, uint32_t pid)
	{
		Neighbors ne;

		for (uint32_t j = 0; j < D.size1(); ++j)
		{
			if 	( D(pid, j) <= m_eps )
			{
				ne.push_back(j);
			}
		}
		return ne;
	}

	void DBSCAN::dbscan( const DBSCAN::DistanceMatrix & dm )
	{
		std::vector<uint8_t> visited( dm.size1() );

		uint32_t cluster_id = 0;

		for (uint32_t pid = 0; pid < dm.size1(); ++pid)
		{
			if ( !visited[pid] )
			{
				visited[pid] = 1;

				Neighbors ne = find_neighbors(dm, pid );

				if (ne.size() >= m_min_elems)
				{
					m_labels[pid] = cluster_id;

					for (uint32_t i = 0; i < ne.size(); ++i)
					{
						uint32_t nPid = ne[i];

						if ( !visited[nPid] )
						{
							visited[nPid] = 1;

							Neighbors ne1 = find_neighbors(dm, nPid);

							if ( ne1.size() >= m_min_elems )
							{
								for (const auto & n1 : ne1)
								{
									ne.push_back(n1);
								}
							}
						}

						if ( m_labels[nPid] == -1 )
						{
							m_labels[nPid] = cluster_id;
						}
					}

					++cluster_id;
				}
			}
		}
	}

	void DBSCAN::fit( const DBSCAN::ClusterData & C )
	{
		const DBSCAN::FeaturesWeights W = DBSCAN::std_weights( C.size2() );
		wfit( C, W );
	}
	void DBSCAN::fit_precomputed( const DBSCAN::DistanceMatrix & D )
	{
		prepare_labels( D.size1() );
		dbscan( D );
	}

	void DBSCAN::wfit( const DBSCAN::ClusterData & C, const DBSCAN::FeaturesWeights & W )
	{
		prepare_labels( C.size1() );
		const DBSCAN::DistanceMatrix D = calc_dist_matrix( C, W );
		dbscan( D );
	}

	const DBSCAN::Labels & DBSCAN::get_labels() const
	{
		return m_labels;
	}

	std::ostream& operator<<(std::ostream& o, DBSCAN & d)
	{
		o << "[ ";
		for ( const auto & l : d.get_labels() )
		{
			o << " " << l;
		}
		o << " ] " << std::endl;

		return o;
	}
}


/*    This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT.
 *    See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details.
 *    Author(s):       Siddharth Pritam, Vincent Rouvreau
 *
 *    Copyright (C) 2020 Inria
 *
 *    Modification(s):
 *      - YYYY/MM Author: Description of the modification
 */

#include <gudhi/Flag_complex_edge_collapser.h>
#include <gudhi/Simplex_tree.h>
#include <gudhi/Persistent_cohomology.h>
#include <gudhi/reader_utils.h>
#include <gudhi/graph_simplicial_complex.h>

#include <boost/program_options.hpp>
#include <boost/range/adaptor/transformed.hpp>

using Simplex_tree = Gudhi::Simplex_tree<Gudhi::Simplex_tree_options_fast_persistence>;
using Filtration_value = Simplex_tree::Filtration_value;
using Vertex_handle = Simplex_tree::Vertex_handle;

using Filtered_edge = std::tuple<Vertex_handle, Vertex_handle, Filtration_value>;
using Proximity_graph = Gudhi::Proximity_graph<Simplex_tree>;

using Field_Zp = Gudhi::persistent_cohomology::Field_Zp;
using Persistent_cohomology = Gudhi::persistent_cohomology::Persistent_cohomology<Simplex_tree, Field_Zp>;
using Distance_matrix = std::vector<std::vector<Filtration_value>>;

void program_options(int argc, char* argv[], std::string& csv_matrix_file, std::string& filediag,
                     Filtration_value& threshold, int& dim_max, int& p, int& edge_collapse_iter_nb,
                     Filtration_value& min_persistence);

int main(int argc, char* argv[]) {
  std::string csv_matrix_file;
  std::string filediag;
  Filtration_value threshold;
  int dim_max = 2;
  int p;
  int edge_collapse_iter_nb;
  Filtration_value min_persistence;

  program_options(argc, argv, csv_matrix_file, filediag, threshold, dim_max, p, edge_collapse_iter_nb,
                  min_persistence);

  Distance_matrix distances = Gudhi::read_lower_triangular_matrix_from_csv_file<Filtration_value>(csv_matrix_file);
  std::cout << "Read the distance matrix succesfully, of size: " << distances.size() << std::endl;

  Proximity_graph proximity_graph = Gudhi::compute_proximity_graph<Simplex_tree>(boost::irange((size_t)0,
                                                                                distances.size()),
                                                                                threshold,
                                                                                [&distances](size_t i, size_t j) {
                                                                                return distances[j][i];
                                                                                });

  auto edges_from_graph = boost::adaptors::transform(edges(proximity_graph), [&](auto&&edge){
        return std::make_tuple(source(edge, proximity_graph),
                               target(edge, proximity_graph),
                               get(Gudhi::edge_filtration_t(), proximity_graph, edge));
      });
  std::vector<Filtered_edge> edges_list(edges_from_graph.begin(), edges_from_graph.end());
  std::vector<Filtered_edge> remaining_edges;
  for (int iter = 0; iter < edge_collapse_iter_nb; iter++) {
    auto remaining_edges = Gudhi::collapse::flag_complex_collapse_edges(edges_list);
    edges_list = std::move(remaining_edges);
    remaining_edges.clear();
  }

  Simplex_tree stree;
  for (Vertex_handle vertex = 0; static_cast<std::size_t>(vertex) < distances.size(); vertex++) {
    // insert the vertex with a 0. filtration value just like a Rips
    stree.insert_simplex({vertex}, 0.);
  }
  for (auto filtered_edge : edges_list) {
    stree.insert_simplex({std::get<0>(filtered_edge), std::get<1>(filtered_edge)}, std::get<2>(filtered_edge));
  }

  stree.expansion(dim_max);

  std::cout << "The complex contains " << stree.num_simplices() << " simplices  after collapse. \n";
  std::cout << "   and has dimension " << stree.dimension() << " \n";

  // Sort the simplices in the order of the filtration
  stree.initialize_filtration();
  // Compute the persistence diagram of the complex
  Persistent_cohomology pcoh(stree);
  // initializes the coefficient field for homology
  pcoh.init_coefficients(3);

  pcoh.compute_persistent_cohomology(min_persistence);
  if (filediag.empty()) {
    pcoh.output_diagram();
  } else {
    std::ofstream out(filediag);
    pcoh.output_diagram(out);
    out.close();
  }
  return 0;
}

void program_options(int argc, char* argv[], std::string& csv_matrix_file, std::string& filediag,
                     Filtration_value& threshold, int& dim_max, int& p, int& edge_collapse_iter_nb,
                     Filtration_value& min_persistence) {
  namespace po = boost::program_options;
  po::options_description hidden("Hidden options");
  hidden.add_options()(
      "input-file", po::value<std::string>(&csv_matrix_file),
      "Name of file containing a distance matrix. Can be square or lower triangular matrix. Separator is ';'.");

  po::options_description visible("Allowed options", 100);
  visible.add_options()("help,h", "produce help message")(
      "output-file,o", po::value<std::string>(&filediag)->default_value(std::string()),
      "Name of file in which the persistence diagram is written. Default print in std::cout")(
      "max-edge-length,r",
      po::value<Filtration_value>(&threshold)->default_value(std::numeric_limits<Filtration_value>::infinity()),
      "Maximal length of an edge for the Rips complex construction.")(
      "cpx-dimension,d", po::value<int>(&dim_max)->default_value(1),
      "Maximal dimension of the Rips complex we want to compute.")(
      "field-charac,p", po::value<int>(&p)->default_value(11),
      "Characteristic p of the coefficient field Z/pZ for computing homology.")(
      "edge-collapse-iterations,i", po::value<int>(&edge_collapse_iter_nb)->default_value(1),
      "Number of iterations edge collapse is performed.")(
      "min-persistence,m", po::value<Filtration_value>(&min_persistence),
      "Minimal lifetime of homology feature to be recorded. Default is 0. Enter a negative value to see zero length "
      "intervals");

  po::positional_options_description pos;
  pos.add("input-file", 1);

  po::options_description all;
  all.add(visible).add(hidden);

  po::variables_map vm;
  po::store(po::command_line_parser(argc, argv).options(all).positional(pos).run(), vm);
  po::notify(vm);

  if (vm.count("help") || !vm.count("input-file")) {
    std::cout << std::endl;
    std::cout << "Compute the persistent homology with coefficient field Z/pZ \n";
    std::cout << "of a Rips complex after edge collapse defined on a set of distance matrix.\n \n";
    std::cout << "The output diagram contains one bar per line, written with the convention: \n";
    std::cout << "   p   dim b d \n";
    std::cout << "where dim is the dimension of the homological feature,\n";
    std::cout << "b and d are respectively the birth and death of the feature and \n";
    std::cout << "p is the characteristic of the field Z/pZ used for homology coefficients." << std::endl << std::endl;

    std::cout << "Usage: " << argv[0] << " [options] input-file" << std::endl << std::endl;
    std::cout << visible << std::endl;
    exit(-1);
  }
}


#ifndef MATHTOOLBOX_GAUSSIAN_PROCESS_REGRESSION_HPP
#define MATHTOOLBOX_GAUSSIAN_PROCESS_REGRESSION_HPP

#include <Eigen/Cholesky>
#include <Eigen/Core>
#include <mathtoolbox/kernel-functions.hpp>

namespace mathtoolbox
{
    class GaussianProcessRegressor
    {
    public:
        enum class KernelType
        {
            ArdSquaredExp,
            ArdMatern52
        };

        /// \brief Construct an instance with input data
        GaussianProcessRegressor(const Eigen::MatrixXd& X,
                                 const Eigen::VectorXd& y,
                                 const KernelType       kernel_type            = KernelType::ArdMatern52,
                                 const bool             use_data_normalization = true);

        /// \brief Calculate the mean of the predicted distribution
        double PredictMean(const Eigen::VectorXd& x) const;

        /// \brief Calculate the standard deviation of the predicted distribution
        double PredictStdev(const Eigen::VectorXd& x) const;

        /// \brief Calculate the derivative of the mean of the predicted distribution
        Eigen::VectorXd PredictMeanDeriv(const Eigen::VectorXd& x) const;

        /// \brief Calculate the derivative of the standard deviation of the predicted distribution
        Eigen::VectorXd PredictStdevDeriv(const Eigen::VectorXd& x) const;

        /// \brief Set hyperparameters directly
        ///
        /// \details Covariance matrix calculation will run within this method.
        void SetHyperparams(const Eigen::VectorXd& kernel_hyperparams, const double noise_hyperparam);

        /// \brief Perform maximum likelihood estimation of the hyperparameters
        void PerformMaximumLikelihood(const Eigen::VectorXd& kernel_hyperparams_initial,
                                      const double           noise_hyperparam_initial);

        /// \brief Get the input data points
        const Eigen::MatrixXd& GetDataPoints() const { return m_X; }

        /// \brief Get the input data values
        const Eigen::VectorXd& GetDataValues() const { return m_y; }

    private:
        // Data points
        Eigen::MatrixXd m_X;
        Eigen::VectorXd m_y;

        // Derivative data
        Eigen::MatrixXd             m_K_y;
        Eigen::LLT<Eigen::MatrixXd> m_K_y_llt;
        Eigen::VectorXd             m_K_y_inv_y;

        // Normalization parameters
        double m_data_mu;
        double m_data_sigma;
        double m_data_scale;

        // Hyperparameters
        Eigen::VectorXd m_kernel_hyperparams;
        double          m_noise_hyperparam;

        // Kernel functions
        Kernel                   m_kernel;
        KernelThetaIDerivative   m_kernel_deriv_theta_i;
        KernelFirstArgDerivative m_kernel_deriv_first_arg;
    };
} // namespace mathtoolbox

#endif // MATHTOOLBOX_GAUSSIAN_PROCESS_REGRESSION_HPP


#include <boost/units/cmath.hpp>
#include <boost/units/io.hpp>
#include <boost/units/quantity.hpp>
#include <boost/units/systems/si.hpp>
#include <iostream>
#include <tuple>

using Length_unit = boost::units::si::length;
using Length = boost::units::quantity<Length_unit>;
using Time_unit = boost::units::si::time;
using Time = boost::units::quantity<Time_unit>;
using Velocity_unit = boost::units::si::velocity;
using Velocity = boost::units::quantity<Velocity_unit>;

Velocity compute_velocity(const Length dx, const Time dt) {
    return dx/dt;
}

using Point = std::tuple<Length, Length>;

Length distance(const Point& p1, const Point& p2) {
    Length x1 {std::get<0>(p1)};
    Length x2 {std::get<0>(p2)};
    Length y1 {std::get<1>(p1)};
    Length y2 {std::get<1>(p2)};
    return sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2));
}

int main() {
    // define units to make formulas easier to read
    Length_unit m {boost::units::si::meter};
    Time_unit s {boost::units::si::seconds};

    Length dx {3.5*m};
    Time dt {2.0*s};
    Velocity velocity = compute_velocity(dx, dt);
    std::cout << velocity << std::endl;
    std::cout << velocity.value() << std::endl;

    Point p1 {std::make_tuple(3.0*m, 4.0*m)};
    Point p2 {std::make_tuple(5.0*m, 2.0*m)};
    std::cout << distance(p1, p2) << std::endl;

    return 0;
}


// STL includes
#include <iostream>
#include <vector>

// BGL includes
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/kruskal_min_spanning_tree.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>

typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS,
  boost::no_property, boost::property<boost::edge_weight_t, int> >      weighted_graph;
typedef boost::property_map<weighted_graph, boost::edge_weight_t>::type weight_map;
typedef boost::graph_traits<weighted_graph>::edge_descriptor            edge_desc;
typedef boost::graph_traits<weighted_graph>::vertex_descriptor          vertex_desc;

using namespace std;

int dijkstra(const weighted_graph &G, int s, int t) {
  int n = boost::num_vertices(G);
  std::vector<int> dist_map(n);

  boost::dijkstra_shortest_paths(G, s,
    boost::distance_map(boost::make_iterator_property_map(
      dist_map.begin(), boost::get(boost::vertex_index, G))));

  return dist_map[t];
}

void kruskal(const weighted_graph &G, vector<edge_desc> &mst) {
  boost::kruskal_minimum_spanning_tree(G, std::back_inserter(mst));
}

void testcase() {
  int n; cin >> n;
  int m; cin >> m;
  int s; cin >> s;
  int a, b; cin >> a; cin >> b;
  vector<weighted_graph> Gs(s, weighted_graph(n));
  weighted_graph G(n);
  vector<weight_map> weights(s);

  for(int i = 0; i < s; i++)
    weights[i] = boost::get(boost::edge_weight, Gs[i]);

  weight_map weights_g = boost::get(boost::edge_weight, G);
  edge_desc e;
  for(int i = 0; i < m; i++) {
    int u,v; cin >> u; cin >> v;
    for(int j = 0; j < s; j++) {
      int w; cin >> w;
      e = boost::add_edge(u, v, Gs[j]).first; weights[j][e] = w;
    }
  }

  int hive; // disregard hives
  for(int i = 0; i < s; i++) {
    cin >> hive;
  }
  for(int i = 0; i < s; i++) {
    vector<edge_desc> mst;
    kruskal(Gs[i], mst);
    for (std::vector<edge_desc>::iterator it = mst.begin(); it != mst.end(); ++it) {
        int u = boost::source(*it, Gs[i]);
        int v = boost::target(*it, Gs[i]);
        if(!edge(u,v,G).second) { // edge not yet in G
          e = boost::add_edge(u, v, G).first;
          weights_g[e] = weights[i][*it];
        } else {
          e = edge(u,v,G).first;
          weights_g[e] = min(weights_g[e], weights[i][*it]);
        }
    }
  }

  cout << dijkstra(G, a, b) << endl;

}
int main()
{
  std::ios_base::sync_with_stdio(false); // Always!
  int t; cin >> t;
  for(int i = 0; i < t; i++)
    testcase();
  return 0;
}