#pragma once
#include <future>
#include <iostream>
#include <Eigen/Dense>
#include <limits.h>
#include "Data.hpp"
#include "tsne/tsne.h"

//Single class for data projection, differnet methods can be set in the constructor
//When created the data is projected in a separate thread
//On completion the "projected" member is set to true
//Call wait on the future object to sync
class DataProjector{
public:
    enum Method{
        PCA,
        TSNE
    };

    struct ProjectionSettings{
        //settings for PCA

        //settings for TSNE
        double perplexity, theta;
        int randSeed, maxIter, stopLyingIter, momSwitchIter;
        bool skipRandomInit;
    };

    DataProjector(const Data& data, int reducedDimensionSize, Method projectionMethod, const ProjectionSettings& settings, std::vector<uint32_t>& indices, std::vector<uint32_t> attributeIndices = {}):data(data), reducedDimensionSize(reducedDimensionSize), projectionMethod(projectionMethod), indices(indices){
        if(attributeIndices.empty()){
            this->attributeIndices.resize(data.columns.size());
            for(uint32_t i = 0; i < indices.size(); ++i) this->attributeIndices[i] = i;
        }
        else this->attributeIndices = attributeIndices;
        this->settings = settings;
        future = std::async(runAsync, this);
    }

    static void runAsync(DataProjector* p){
        p->run();
    }

    void run(){
        switch(projectionMethod){
        case Method::PCA:{
            execPCA();
        } break;
        case Method::TSNE:{
            execTSNE();
        }break;
        }
    }

    Eigen::MatrixXf projectedPoints;
    bool projected = false;
    bool interrupted = false;
    float progress = .0f;
    int reducedDimensionSize;
    std::future<void> future;

protected:
    const Data& data;
    std::vector<uint32_t> attributeIndices, &indices;
    ProjectionSettings settings;
    const float eps = 1e-6;
    Method projectionMethod;
    Eigen::MatrixXf getDataMatrix(){
        //data to eigen matrix
        Eigen::MatrixXf d(indices.size(), attributeIndices.size());
        for(int i = 0; i < indices.size(); ++i){
            for(int  c = 0; c < attributeIndices.size(); ++c){
                d(i,c) = data(indices[i],attributeIndices[c]);
            }
        }
        //zero center and normalize
        Eigen::RowVectorXf meanCols = d.colwise().mean();
        d = d.rowwise() - meanCols;
        Eigen::RowVectorXf mins = d.colwise().minCoeff(), diff = d.colwise().maxCoeff() - mins;
        diff.array() += eps;
        d.array().rowwise() /= diff.array();
        progress = .33f;
        return d;
    }

    void normalizeProjectedPoints(){
        Eigen::RowVectorXf min = projectedPoints.colwise().minCoeff();
        Eigen::RowVectorXf diff = projectedPoints.colwise().maxCoeff() - min;
        projectedPoints.rowwise() -= min;
        projectedPoints.array().rowwise() /= diff.array();
    }

    void execPCA(){
        Eigen::MatrixXf d = getDataMatrix();
        Eigen::BDCSVD<Eigen::MatrixXf> svd(d, Eigen::ComputeThinU | Eigen::ComputeThinV);
        progress = .66f;
        //convert points to pc scores
        auto u = svd.matrixU().real();
        projectedPoints = u * svd.singularValues().real().asDiagonal();
        //drop unused scores and normalizing the points
        projectedPoints.conservativeResize(Eigen::NoChange, reducedDimensionSize);
        normalizeProjectedPoints();
        progress = 1;
        projected = true;
    }

    void execTSNE(){
        // usage of exact algorithm
        if(data.size() - 1 < 3 * settings.perplexity){
            std::cout << "Perplexity too large for the number of data points!" << std::endl;
            return;
        }
        bool exact = settings.theta == .0f;
        if(exact && data.size() * data.size() > INT_MAX){
            std::cout << "Too large dataset for exact computation, use a theta > 0 for approximation" << std::endl;
            progress = 1;
            interrupted = true;
            return;
        }

        Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> d = getDataMatrix().cast<double>();
        Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> y(d.rows(), reducedDimensionSize);

        //checking rowmajorness of d
        //assert(d(0,0) == d.data()[0]);
        //assert(d(0,1) == d.data()[1]);
        //assert(d(0,2) == d.data()[2]);
        //assert(d(1,0) == d.data()[3]);
        //std::cout << d(0,0) << " " << d.data()[0] << std::endl;
        //std::cout << d(0,1) << " " << d.data()[1] << std::endl;
        //std::cout << d(0,2) << " " << d.data()[2] << std::endl;
        //std::cout << d(1,0) << " " << d.data()[3] << std::endl;
        //std::cout << d(2,0) << " " << d.data()[6] << std::endl;

        TSNE::run(d.data(), d.rows(), d.cols(), y.data(), reducedDimensionSize, settings.perplexity, settings.theta, settings.randSeed, settings.skipRandomInit, settings.maxIter, settings.stopLyingIter, settings.momSwitchIter, &progress);
        if(progress == -1){
            interrupted = true;
            return;
        }
        projectedPoints = y.cast<float>();
        normalizeProjectedPoints();
        progress = 1;
        projected = true;
    }
};


// Copyright Christopher Kormanyos 2013.
// Copyright Paul A. Bristow 2013.
// Copyright John Maddock 2013.

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

#ifdef _MSC_VER
#  pragma warning (disable : 4512) // assignment operator could not be generated.
#  pragma warning (disable : 4996) // assignment operator could not be generated.
#endif

#include <iostream>
#include <limits>
#include <vector>
#include <algorithm>
#include <iomanip>
#include <exception>

// Weisstein, Eric W. "Bessel Function Zeros." From MathWorld--A Wolfram Web Resource.
// http://mathworld.wolfram.com/BesselFunctionZeros.html
// Test values can be calculated using [@wolframalpha.com WolframAplha]
// See also http://dlmf.nist.gov/10.21

//[bessel_errors_example_1

/*`[h5 Error messages from 'bad' input]

Another example demonstrates calculating zeros of the Bessel functions
showing the error messages from 'bad' input is handled by throwing exceptions.

To use the functions for finding zeros of the functions we need:
*/
  #include <boost/math/special_functions/bessel.hpp>
  #include <boost/math/special_functions/airy.hpp>

//] [/bessel_errors_example_1]

int main()
{
//[bessel_errors_example_2

/*`[tip It is always wise to place all code using Boost.Math inside try'n'catch blocks;
this will ensure that helpful error messages can be shown when exceptional conditions arise.]

Examples below show messages from several 'bad' arguments that throw a `domain_error` exception.
*/
  try
  { // Try a zero order v.
    float dodgy_root = boost::math::cyl_bessel_j_zero(0.F, 0);
    std::cout << "boost::math::cyl_bessel_j_zero(0.F, 0) " << dodgy_root << std::endl;
    // Thrown exception Error in function boost::math::cyl_bessel_j_zero<double>(double, int):
    // Requested the 0'th zero of J0, but the rank must be > 0 !
  }
  catch (std::exception& ex)
  {
    std::cout << "Thrown exception " << ex.what() << std::endl;
  }

/*`[note The type shown in the error message is the type [*after promotion],
using __precision_policy and __promotion_policy, from `float` to `double` in this case.]

In this example the promotion goes:

# Arguments are `float` and `int`.
# Treat `int` "as if" it were a `double`, so arguments are `float` and `double`.
# Common type is `double` - so that's the precision we want (and the type that will be returned).
# Evaluate internally as `double` for full `float` precision.

See full code for other examples that promote from `double` to `long double`.

Other examples of 'bad' inputs like infinity and NaN are below.
Some compiler warnings indicate that 'bad' values are detected at compile time.
*/

  try
  { // order v = inf
     std::cout << "boost::math::cyl_bessel_j_zero(inf, 1) " << std::endl;
     double inf = std::numeric_limits<double>::infinity();
     double inf_root = boost::math::cyl_bessel_j_zero(inf, 1);
     std::cout << "boost::math::cyl_bessel_j_zero(inf, 1) " << inf_root << std::endl;
     // Throw exception Error in function boost::math::cyl_bessel_j_zero<long double>(long double, unsigned):
     // Order argument is 1.#INF, but must be finite >= 0 !
  }
  catch (std::exception& ex)
  {
    std::cout << "Thrown exception " << ex.what() << std::endl;
  }

  try
  { // order v = NaN, rank m = 1
     std::cout << "boost::math::cyl_bessel_j_zero(nan, 1) " << std::endl;
     double nan = std::numeric_limits<double>::quiet_NaN();
     double nan_root = boost::math::cyl_bessel_j_zero(nan, 1);
     std::cout << "boost::math::cyl_bessel_j_zero(nan, 1) " << nan_root << std::endl;
     // Throw exception Error in function boost::math::cyl_bessel_j_zero<long double>(long double, unsigned):
     // Order argument is 1.#QNAN, but must be finite >= 0 !
  }
  catch (std::exception& ex)
  {
    std::cout << "Thrown exception " << ex.what() << std::endl;
  }

/*`The output from other examples are shown appended to the full code listing.
*/
//] [/bessel_errors_example_2]
  try
  {   // Try a zero rank m.
    std::cout << "boost::math::cyl_neumann_zero(0.0, 0) " << std::endl;
    double dodgy_root = boost::math::cyl_bessel_j_zero(0.0, 0);
    //  warning C4146: unary minus operator applied to unsigned type, result still unsigned.
    std::cout << "boost::math::cyl_neumann_zero(0.0, -1) " << dodgy_root << std::endl;
    //  boost::math::cyl_neumann_zero(0.0, -1) 6.74652e+009
    // This *should* fail because m is unreasonably large.

  }
  catch (std::exception& ex)
  {
    std::cout << "Thrown exception " << ex.what() << std::endl;
  }

  try
  { // m = inf
   std::cout << "boost::math::cyl_bessel_j_zero(0.0, inf) " << std::endl;
   double inf = std::numeric_limits<double>::infinity();
     double inf_root = boost::math::cyl_bessel_j_zero(0.0, inf);
     // warning C4244: 'argument' : conversion from 'double' to 'int', possible loss of data.
     std::cout << "boost::math::cyl_bessel_j_zero(0.0, inf) " << inf_root << std::endl;
     // Throw exception Error in function boost::math::cyl_bessel_j_zero<long double>(long double, int):
     // Requested the 0'th zero, but must be > 0 !

  }
  catch (std::exception& ex)
  {
    std::cout << "Thrown exception " << ex.what() << std::endl;
  }

  try
  { // m = NaN
     double nan = std::numeric_limits<double>::quiet_NaN();
     double nan_root = boost::math::airy_ai_zero<double>(nan);
     // warning C4244: 'argument' : conversion from 'double' to 'int', possible loss of data.
     std::cout << "boost::math::airy_ai_zero<double>(nan) " << nan_root << std::endl;
     // Thrown exception Error in function boost::math::airy_ai_zero<double>(double,double):
     // The requested rank of the zero is 0, but must be 1 or more !
  }
  catch (std::exception& ex)
  {
    std::cout << "Thrown exception " << ex.what() << std::endl;
  }
 } // int main()

/*
Output:

  Description: Autorun "J:\Cpp\big_number\Debug\bessel_errors_example.exe"
  Thrown exception Error in function boost::math::cyl_bessel_j_zero<double>(double, int): Requested the 0'th zero of J0, but the rank must be > 0 !
  boost::math::cyl_bessel_j_zero(inf, 1)
  Thrown exception Error in function boost::math::cyl_bessel_j_zero<long double>(long double, int): Order argument is 1.#INF, but must be finite >= 0 !
  boost::math::cyl_bessel_j_zero(nan, 1)
  Thrown exception Error in function boost::math::cyl_bessel_j_zero<long double>(long double, int): Order argument is 1.#QNAN, but must be finite >= 0 !
  boost::math::cyl_neumann_zero(0.0, 0)
  Thrown exception Error in function boost::math::cyl_bessel_j_zero<long double>(long double, int): Requested the 0'th zero of J0, but the rank must be > 0 !
  boost::math::cyl_bessel_j_zero(0.0, inf)
  Thrown exception Error in function boost::math::cyl_bessel_j_zero<long double>(long double, int): Requested the -2147483648'th zero, but the rank must be positive !
  Thrown exception Error in function boost::math::airy_ai_zero<double>(double,double): The requested rank of the zero is 0, but must be 1 or more !

*/


/* test_uniform_on_sphere.cpp
 *
 * Copyright Steven Watanabe 2011
 * Distributed under the Boost Software License, Version 1.0. (See
 * accompanying file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 *
 * $Id$
 *
 */

#include <boost/random/uniform_on_sphere.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/math/distributions/uniform.hpp>
#include <cmath>

class uniform_on_sphere_test {
public:
    typedef double result_type;
    uniform_on_sphere_test(int dims, int x, int y)
        : impl(dims), idx1(x), idx2(y) {}
    template<class Engine>
    result_type operator()(Engine& rng) {
        const boost::random::uniform_on_sphere<>::result_type& tmp = impl(rng);
        // This should be uniformly distributed in [-pi,pi)
        return std::atan2(tmp[idx1], tmp[idx2]);
    }
private:
    boost::random::uniform_on_sphere<> impl;
    int idx1, idx2;
};

static const double pi = 3.14159265358979323846;

#define BOOST_RANDOM_DISTRIBUTION uniform_on_sphere_test
#define BOOST_RANDOM_DISTRIBUTION_NAME uniform_on_sphere
#define BOOST_MATH_DISTRIBUTION boost::math::uniform
#define BOOST_RANDOM_ARG1_TYPE double
#define BOOST_RANDOM_ARG1_NAME n
#define BOOST_RANDOM_ARG1_DEFAULT 6
#define BOOST_RANDOM_ARG1_DISTRIBUTION(n) boost::uniform_int<>(2, n)
#define BOOST_RANDOM_DISTRIBUTION_INIT (n, 0, n-1)
#define BOOST_MATH_DISTRIBUTION_INIT (-pi, pi)

#include "test_real_distribution.ipp"


/* @copyright The code is licensed under the MIT License
 *            <https://opensource.org/licenses/MIT>,
 *            Copyright (c) 2020 Christian Eskil Vaugelade Berg
 * @author Christian Eskil Vaugelade Berg
*/
#pragma once

#include <utility>

#include <Eigen/Dense>

namespace orient {

// \brief Calculate quaternion from angle axis
//        Quaternion is expressed as a 4D-vector [w,a,b,c] such that
//        q = w + a*i + b*j + c*k
// \param aa Source angle axis
// \return The quaternion
template<typename Scalar>
Eigen::Matrix<Scalar, 4, 1> quaternionFromAngleAxis(Eigen::Matrix<Scalar, 3, 1> const& aa);

// \brief Calculate quaternion and partial derivatives from angle axis
//        See above for more detail
// \param aa Source angle axis
// \return A pair containg first the quaternion then secondly the Jacobian matrix
template<typename Scalar>
std::pair<Eigen::Matrix<Scalar, 4, 1>, Eigen::Matrix<Scalar, 4, 3>> quaternionFromAngleAxisWD(Eigen::Matrix<Scalar,3, 1> const& aa);

// \brief Calculate rotation matrix from angle axis
// \param aa Source angle axis
// \return Rotation matrix
template<typename Scalar>
Eigen::Matrix<Scalar, 3, 3> rotationMatrixFromAngleAxis(Eigen::Matrix<Scalar,3, 1> const& aa);

// \brief Calculate rotation matrix and partial derivatives from angle axis
// \param aa Source angle axis
// \return A pair containg first the rotation matrix then secondly the Jacobian matrix
template<typename Scalar>
std::pair<Eigen::Matrix<Scalar, 3, 3>, Eigen::Matrix<Scalar, 9, 3>> rotationMatrixFromAngleAxisWD(Eigen::Matrix<Scalar, 3, 1> const& aa);

}

#include <orient/impl/from_angle_axis.hpp>


// Boost.Geometry (aka GGL, Generic Geometry Library)
// QuickBook Example

// Copyright (c) 2011-2012 Barend Gehrels, Amsterdam, the Netherlands.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

//[transform_with_strategy
//` Shows how points can be scaled, translated or rotated

#include <iostream>
#include <boost/geometry.hpp>

int main()
{
    namespace trans = boost::geometry::strategy::transform;
    using boost::geometry::dsv;

    typedef boost::geometry::model::point<double, 2, boost::geometry::cs::cartesian> point_type;

    point_type p1(1.0, 1.0);

    // Translate over (1.5, 1.5)
    point_type p2;
    trans::translate_transformer<double, 2, 2> translate(1.5, 1.5);
    boost::geometry::transform(p1, p2, translate);

    // Scale with factor 3.0
    point_type p3;
    trans::scale_transformer<double, 2, 2> scale(3.0);
    boost::geometry::transform(p1, p3, scale);

    // Rotate with respect to the origin (0,0) over 90 degrees (clockwise)
    point_type p4;
    trans::rotate_transformer<boost::geometry::degree, double, 2, 2> rotate(90.0);
    boost::geometry::transform(p1, p4, rotate);

    std::cout
        << "p1: " << dsv(p1) << std::endl
        << "p2: " << dsv(p2) << std::endl
        << "p3: " << dsv(p3) << std::endl
        << "p4: " << dsv(p4) << std::endl;

    return 0;
}

//]

//[transform_with_strategy_output
/*`
Output:
[pre
p1: (1, 1)
p2: (2.5, 2.5)
p3: (3, 3)
p4: (1, -1)
]
*/
//]


// solving A * X = B
// using driver function gesv()
// with c_vector<> & c_matrix<>

//#define BOOST_NUMERIC_BINDINGS_POOR_MANS_TRAITS
//#define BOOST_NO_FUNCTION_TEMPLATE_ORDERING

//#define BOOST_NUMERIC_BINDINGS_NO_SANITY_CHECK
//#define BOOST_NUMERIC_BINDINGS_NO_STRUCTURE_CHECK

#include <cstddef>
#include <iostream>
#include <boost/numeric/bindings/atlas/cblas.hpp>
#include <boost/numeric/bindings/atlas/clapack.hpp>
#ifndef BOOST_NUMERIC_BINDINGS_POOR_MANS_TRAITS
#  include <boost/numeric/bindings/traits/ublas_vector2.hpp>
#endif
#include <boost/numeric/bindings/traits/ublas_matrix.hpp>
#include <boost/numeric/ublas/matrix_proxy.hpp>
#include <boost/numeric/ublas/io.hpp>

namespace ublas = boost::numeric::ublas;
namespace atlas = boost::numeric::bindings::atlas;

using std::size_t;
using std::cout;
using std::endl;

typedef ublas::c_matrix<double, 4, 4> m4x4_t;
typedef ublas::c_matrix<double, 3, 3> m3x3_t;
typedef ublas::c_matrix<double, 1, 3> mrhs_t;
typedef ublas::c_vector<double, 5> v5_t;

int main() {

  cout << endl;
  size_t n = 3;

  m4x4_t a (n, n);   // system matrix
  a(0,0) = 1.; a(0,1) = 1.; a(0,2) = 1.;
  a(1,0) = 2.; a(1,1) = 3.; a(1,2) = 1.;
  a(2,0) = 1.; a(2,1) = -1.; a(2,2) = -1.;

  mrhs_t b (1, n);  // right-hand side vector
  b(0,0) = 4.; b(0,1) = 9.; b(0,2) = -2.;

#ifndef BOOST_NUMERIC_BINDINGS_POOR_MANS_TRAITS
  m3x3_t a2; // for part 2
  a2 = project (a, ublas::range (0,3), ublas::range (0,3));
  v5_t b2 (n);
  b2 = row (b, 0);
#endif

  // part 1:
  cout << "A: " << a << endl;
  cout << "B: " << b << endl;

  atlas::lu_solve (a, b);
  cout << "X: " << b << endl;

  cout << endl;

#ifndef BOOST_NUMERIC_BINDINGS_POOR_MANS_TRAITS
  // part 2:
  cout << "A: " << a2 << endl;
  cout << "B: " << b2 << endl;

  atlas::lu_solve (a2, b2);
  cout << "X: " << b2 << endl;

  cout << endl;
#endif
}


#ifndef STAN_MATH_FWD_SCAL_FUN_FALLING_FACTORIAL_HPP
#define STAN_MATH_FWD_SCAL_FUN_FALLING_FACTORIAL_HPP

#include <stan/math/fwd/core.hpp>

#include <stan/math/prim/scal/fun/falling_factorial.hpp>
#include <boost/math/special_functions/digamma.hpp>

namespace stan {
  namespace math {

    template<typename T>
    inline fvar<T>
    falling_factorial(const fvar<T>& x, const fvar<T>& n) {
      using boost::math::digamma;

      T falling_fact(falling_factorial(x.val_, n.val_));
      return fvar<T>(falling_fact,
                     falling_fact
                     * (digamma(x.val_ + 1) - digamma(x.val_ - n.val_ + 1))
                     * x.d_
                     + falling_fact
                     * digamma(x.val_ - n.val_ + 1) * n.d_);
    }

    template<typename T>
    inline fvar<T>
    falling_factorial(const fvar<T>& x, double n) {
      using boost::math::digamma;

      T falling_fact(falling_factorial(x.val_, n));
      return fvar<T>(falling_fact,
                     falling_fact
                     * (digamma(x.val_ + 1) - digamma(x.val_ - n + 1))
                     * x.d_);
    }

    template<typename T>
    inline fvar<T>
    falling_factorial(double x, const fvar<T>& n) {
      using boost::math::digamma;

      T falling_fact(falling_factorial(x, n.val_));
      return fvar<T>(falling_fact,
                     falling_fact
                     * digamma(x - n.val_ + 1) * n.d_);
    }
  }
}
#endif


#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();

}


/*
    mvee.hpp
    Desc: Computes minimum volume ellipsoid
    @author Chris Larson, Cornell University
    @date (2017)
    @version 1.0
*/

#ifndef MVEE_HPP_
#define MVEE_HPP_

#include <string>
#include <vector>
#include <Eigen/Dense>

namespace mvee {

class Mvee {

private:

	std::vector<double> _centroid;

	std::vector<double> _radii;

	std::vector<std::vector<double>> _pose;

	void decompose(Eigen::MatrixXd&,
			 	   Eigen::VectorXd&,
			 	   Eigen::MatrixXd&,
			 	   Eigen::MatrixXd&);

	void khachiyan(Eigen::MatrixXd&, double, double);

public:

	Mvee();

	double time;

	long int iters;

	std::vector<double> centroid();

	std::vector<double> radii();

	std::vector<std::vector<double>> pose();

	void compute(std::vector<std::vector<double>>&, double, double);

	void compute(Eigen::MatrixXd&, double, double);

	void compute(std::string, char, double, double);

	~Mvee();

};

}

#endif /* MVEE_HPP_ */


#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;
    }

}


#define NUM_BITS_REAL_MANTISSA 128
#include <cstdint>
#include <cmath>
#include <NTL/ZZ.h>

#include "binomials.hpp"

int main(int argc, char* argv[]){
 if(argc != 3){
     std::cout << "Calculator to derive the length of the encodable bit string via CW-enc" << std::endl << " Usage "
               << argv[0] << " <codeword_size> <number_of_errors> " << std::endl;
    return -1;
 }

 InitBinomials();
 NTL::RR::SetPrecision(NUM_BITS_REAL_MANTISSA);
 pi = NTL::ComputePi_RR();
 uint32_t n = atoi(argv[1]);
 uint32_t t = atoi(argv[2]);
 /* reduce by a factor matching the QC block size */
 NTL::RR encodable_length;
 encodable_length = lnBinom(NTL::to_RR(n), NTL::to_RR(t))/NTL::log(NTL::RR(2));

 NTL::RR d = NTL::to_RR( 0.69315 * ((double)n - ( (double)t - 1.0)/2.0) /((double) t) );

 std::cout << "Maximum safely encoded: " << t*NTL::conv<unsigned long int>(NTL::floor(NTL::log(d)/NTL::log(NTL::RR(2))+1)) << std::endl;
 std::cout << "#define MAX_ENCODABLE_BIT_SIZE_CW_ENCODING (" << NTL::conv<unsigned long int>(encodable_length) << ")" ;
  std::cout << std::endl;
  return 0;
}


/*
 * 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;

}


// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008-2012 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include "main.h"
#include <Eigen/Geometry>
#include <Eigen/LU>
#include <Eigen/SVD>

template<typename Scalar>
void verify_euler(const Matrix<Scalar,3,1>& ea, int i, int j, int k)
{
  typedef Matrix<Scalar,3,3> Matrix3;
  typedef Matrix<Scalar,3,1> Vector3;
  typedef AngleAxis<Scalar> AngleAxisx;
  using std::abs;
  Matrix3 m(AngleAxisx(ea[0], Vector3::Unit(i)) * AngleAxisx(ea[1], Vector3::Unit(j)) * AngleAxisx(ea[2], Vector3::Unit(k)));
  Vector3 eabis = m.eulerAngles(i, j, k);
  Matrix3 mbis(AngleAxisx(eabis[0], Vector3::Unit(i)) * AngleAxisx(eabis[1], Vector3::Unit(j)) * AngleAxisx(eabis[2], Vector3::Unit(k)));
  VERIFY_IS_APPROX(m,  mbis);
  /* If I==K, and ea[1]==0, then there no unique solution. */
  /* The remark apply in the case where I!=K, and |ea[1]| is close to pi/2. */
  if( (i!=k || ea[1]!=0) && (i==k || !internal::isApprox(abs(ea[1]),Scalar(M_PI/2),test_precision<Scalar>())) )
    VERIFY((ea-eabis).norm() <= test_precision<Scalar>());

  // approx_or_less_than does not work for 0
  VERIFY(0 < eabis[0] || test_isMuchSmallerThan(eabis[0], Scalar(1)));
  VERIFY_IS_APPROX_OR_LESS_THAN(eabis[0], Scalar(M_PI));
  VERIFY_IS_APPROX_OR_LESS_THAN(-Scalar(M_PI), eabis[1]);
  VERIFY_IS_APPROX_OR_LESS_THAN(eabis[1], Scalar(M_PI));
  VERIFY_IS_APPROX_OR_LESS_THAN(-Scalar(M_PI), eabis[2]);
  VERIFY_IS_APPROX_OR_LESS_THAN(eabis[2], Scalar(M_PI));
}

template<typename Scalar> void check_all_var(const Matrix<Scalar,3,1>& ea)
{
  verify_euler(ea, 0,1,2);
  verify_euler(ea, 0,1,0);
  verify_euler(ea, 0,2,1);
  verify_euler(ea, 0,2,0);

  verify_euler(ea, 1,2,0);
  verify_euler(ea, 1,2,1);
  verify_euler(ea, 1,0,2);
  verify_euler(ea, 1,0,1);

  verify_euler(ea, 2,0,1);
  verify_euler(ea, 2,0,2);
  verify_euler(ea, 2,1,0);
  verify_euler(ea, 2,1,2);
}

template<typename Scalar> void eulerangles()
{
  typedef Matrix<Scalar,3,3> Matrix3;
  typedef Matrix<Scalar,3,1> Vector3;
  typedef Array<Scalar,3,1> Array3;
  typedef Quaternion<Scalar> Quaternionx;
  typedef AngleAxis<Scalar> AngleAxisx;

  Scalar a = internal::random<Scalar>(-Scalar(M_PI), Scalar(M_PI));
  Quaternionx q1;
  q1 = AngleAxisx(a, Vector3::Random().normalized());
  Matrix3 m;
  m = q1;

  Vector3 ea = m.eulerAngles(0,1,2);
  check_all_var(ea);
  ea = m.eulerAngles(0,1,0);
  check_all_var(ea);

  // Check with purely random Quaternion:
  q1.coeffs() = Quaternionx::Coefficients::Random().normalized();
  m = q1;
  ea = m.eulerAngles(0,1,2);
  check_all_var(ea);
  ea = m.eulerAngles(0,1,0);
  check_all_var(ea);

  // Check with random angles in range [0:pi]x[-pi:pi]x[-pi:pi].
  ea = (Array3::Random() + Array3(1,0,0))*Scalar(M_PI)*Array3(0.5,1,1);
  check_all_var(ea);

  ea[2] = ea[0] = internal::random<Scalar>(0,Scalar(M_PI));
  check_all_var(ea);

  ea[0] = ea[1] = internal::random<Scalar>(0,Scalar(M_PI));
  check_all_var(ea);

  ea[1] = 0;
  check_all_var(ea);

  ea.head(2).setZero();
  check_all_var(ea);

  ea.setZero();
  check_all_var(ea);
}

void test_geo_eulerangles()
{
  for(int i = 0; i < g_repeat; i++) {
    CALL_SUBTEST_1( eulerangles<float>() );
    CALL_SUBTEST_2( eulerangles<double>() );
  }
}


#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);
}


/* @file
 * @copyright This code is licensed under the 3-clause BSD license.
 *   Copyright ETH Zurich, Laboratory of Physical Chemistry, Reiher Group.
 *   See LICENSE.txt for details.
 */

#include <boost/test/unit_test.hpp>

#include "Molassembler/Shapes/Diophantine.h"

using namespace Scine::Molassembler::Shapes;

BOOST_AUTO_TEST_CASE(DiophantineExample, *boost::unit_test::label("Shapes")) {
  std::vector<unsigned> x;
  const std::vector<unsigned> a {4, 3, 2};
  const int b = 12;

  const std::vector<
    std::vector<unsigned>
  > expectedX {
    {0, 0, 6},
    {0, 2, 3},
    {0, 4, 0},
    {1, 0, 4},
    {1, 2, 1},
    {2, 0, 2},
    {3, 0, 0}
  };

  BOOST_REQUIRE(Diophantine::first_solution(x, a, b));
  unsigned i = 0;
  do {
    BOOST_CHECK(x == expectedX.at(i));
    ++i;
  } while(Diophantine::next_solution(x, a, b));
  BOOST_REQUIRE_EQUAL(i, expectedX.size());
  BOOST_REQUIRE(x == std::vector<unsigned> (3, 0));
}


#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 )
  ;
}