// Created by A. Aichert on Tue Aug 12th 2014
#ifndef __model_similarity3D
#define __model_similarity3D

#include <LibOpterix/ParameterModel.hxx>

#include <LibProjectiveGeometry/ProjectiveGeometry.hxx>

#include <Eigen/Geometry>

namespace Geometry {
	// Parametrization for a similarity transformation of 3-space
	struct ModelSimilarity3D : public LibOpterix::ParameterModel<Geometry::RP3Homography>
	{
		/// Human readable names of all parameters.
		static const std::vector<std::string>& ParameterNames()
		{
			static std::vector<std::string> names;
			if (names.empty())
			{
				names.resize(7);
				names[ 0]="Translation X";
				names[ 1]="Translation Y";
				names[ 2]="Translation Z";
				names[ 3]="Rotation X";
				names[ 4]="Rotation Y";
				names[ 5]="Rotation Z";
				names[ 6]="3D Scale";
			}
			return names;
		}

		/// Frequent sets of active parameters
		static const LibOpterix::ParameterSets& ParameterSets()
		{
			static LibOpterix::ParameterSets sets;
			if (sets.empty())
			{
				sets["3D Translation"].insert(0);
				sets["3D Translation"].insert(1);
				sets["3D Translation"].insert(2);
				sets["3D Rigid"].insert(0);
				sets["3D Rigid"].insert(1);
				sets["3D Rigid"].insert(2);
				sets["3D Rigid"].insert(3);
				sets["3D Rigid"].insert(4);
				sets["3D Rigid"].insert(5);
				sets["3D Similarity"].insert(0);
				sets["3D Similarity"].insert(1);
				sets["3D Similarity"].insert(2);
				sets["3D Similarity"].insert(3);
				sets["3D Similarity"].insert(4);
				sets["3D Similarity"].insert(5);
				sets["3D Similarity"].insert(6);
			}
			return sets;
		}

		ModelSimilarity3D(std::set<int> _active=std::set<int>())
			: LibOpterix::ParameterModel<Geometry::RP3Homography>(ParameterNames(),_active)
		{}

		// Compose a 3D homography from parametrization
		virtual Geometry::RP3Homography getInstance() const
		{
			using namespace Eigen;
			// Slicker naming of the parameter vector.
			const std::vector<double>& x(current_values);
			// We begin with an identity
			Matrix4d T=Matrix4d::Identity();
			// Rotation about X Y and Z
			if (x[3]!=0||x[4]!=0||x[5]!=0)
			{
				auto R=( AngleAxisd(x[3], Vector3d::UnitX())
						*AngleAxisd(x[4], Vector3d::UnitY())
						*AngleAxisd(x[5], Vector3d::UnitZ()));
				T.block<3,3>(0,0)=(Matrix3d)R;
			}
			// Center of rotation and translation
			T(0,3)=x[0];
			T(1,3)=x[1];
			T(2,3)=x[2];
			// Apply scaling
			if (x[6]!=0) T.block<3,3>(0,0)*=(1.0+x[6]);
			// Return result
			return T;
		}

	};

} // namespace Geometry

#endif // __model_similarity3D


//
// Created by krab1k on 31/10/18.
//

#include <vector>
#include <functional>
#include <Eigen/LU>

#include "eem.h"
#include "../parameters.h"
#include "../geometry.h"

CHARGEFW2_METHOD(EEM)

Eigen::VectorXd EEM::EE_system(const std::vector<const Atom *> &atoms, double total_charge) const {

    size_t n = atoms.size();

    Eigen::MatrixXd A = Eigen::MatrixXd::Zero(n + 1, n + 1);
    Eigen::VectorXd b = Eigen::VectorXd::Zero(n + 1);

    for (size_t i = 0; i < n; i++) {
        const auto &atom_i = *atoms[i];
        A(i, i) = parameters_->atom()->parameter(atom::B)(atom_i);
        b(i) = -parameters_->atom()->parameter(atom::A)(atom_i);
        for (size_t j = i + 1; j < n; j++) {
            const auto &atom_j = *atoms[j];
            auto x = parameters_->common()->parameter(common::kappa) / distance(atom_i, atom_j);
            A(i, j) = x;
            A(j, i) = x;
        }
    }
    A.row(n) = Eigen::VectorXd::Constant(n + 1, 1);
    A.col(n) = Eigen::VectorXd::Constant(n + 1, 1);
    A(n, n) = 0;
    b(n) = total_charge;

    return A.partialPivLu().solve(b).head(n);
}

std::vector<double> EEM::calculate_charges(const Molecule &molecule) const {
    auto f = [this](const std::vector<const Atom *> &atoms, double total_charge) -> Eigen::VectorXd {
        return EE_system(atoms, total_charge);
    };

    Eigen::VectorXd q = solve_EE(molecule, f);
    return std::vector<double>(q.data(), q.data() + q.size());
}


/* Copyright (c) 2015, Julian Straub <jstraub@csail.mit.edu>, Randi Cabezas <rcabezas@csail.mit.edu>
 * Licensed under the MIT license. See the license file LICENSE.
 */

#pragma once

#include <Eigen/Dense>

#include <boost/math/special_functions/gamma.hpp>
#include <boost/math/distributions/inverse_gamma.hpp>

#include <boost/random/chi_squared_distribution.hpp>
#include <boost/random/normal_distribution.hpp>

#include <dpMM/distribution.hpp>
#include <dpMM/normal.hpp>

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

template<typename T>
class NIW : public Distribution<T>
{
public:
  Matrix<T,Dynamic,Dynamic> Delta_;
  Matrix<T,Dynamic,1> theta_;
  T nu_,kappa_;
  uint32_t D_;

  NIW(const Matrix<T,Dynamic,Dynamic>& Delta,
    const Matrix<T,Dynamic,Dynamic>& theta, T nu,  T kappa,
    boost::mt19937 *pRndGen);
  NIW(const NIW& niw);
  ~NIW();

  NIW<T>* copy();

  NIW<T> posterior(const Matrix<T,Dynamic,Dynamic>& x, const VectorXu& z,
    uint32_t k);
  NIW<T> posterior(const vector<Matrix<T,Dynamic,Dynamic> >&x, const
      VectorXu& z, uint32_t k);
  // assumes vector [N, sum(x), flatten(sum(outer(x,x)))]
  NIW<T> posteriorFromSS(const Matrix<T,Dynamic,1>& x);
  NIW<T> posteriorFromSS(const vector<Matrix<T,Dynamic,1> >&x, const
      VectorXu& z, uint32_t k);

  NIW<T> posterior() const;
  void resetSufficientStatistics();
  void getSufficientStatistics(const Matrix<T,Dynamic,Dynamic> &x,
    const VectorXu& z, uint32_t k);
  void getSufficientStatistics(const vector<Matrix<T,Dynamic,Dynamic> > &x,
    const VectorXu& z, uint32_t k);
  T logProb(const Matrix<T,Dynamic,Dynamic>& x_i) const;
  T logPosteriorProb(const Matrix<T,Dynamic,Dynamic>& x, VectorXu& z, uint32_t k,
    uint32_t i);

  Normal<T> sample();
  Normal<T> sampleFromPosterior();

  T logPdf(const Normal<T>& normal) const;
  T logPdfMarginalized() const; // log pdf of SS under NIW prior
  T logPdfUnderPriorMarginalizedMerged(const NIW<T>& other) const;

  T logLikelihoodMarginalized(const Matrix<T,Dynamic,Dynamic>& Scatter,
      const Matrix<T,Dynamic,1>& mean, T count) const;
  void print() const;

  virtual NIW<T>* merge(const NIW<T>& other);
  void fromMerge(const NIW<T>& niwA, const NIW<T>& niwB);

  const Matrix<T,Dynamic,Dynamic>& scatter() const {return scatter_;};
  Matrix<T,Dynamic,Dynamic>& scatter() {return scatter_;};
  const Matrix<T,Dynamic,1>& mean() const {return mean_;};
  Matrix<T,Dynamic,1>& mean() {return mean_;};
  T count() const {return count_;};
  T& count() {return count_;};

  void computeMergedSS( const NIW<T>& niwA,
    const NIW<T>& niwB, Matrix<T,Dynamic,Dynamic>& scatterM,
    Matrix<T,Dynamic,1>& muM, T& countM) const;

private:
  boost::random::normal_distribution<> gauss_;

  // sufficient statistics
  Matrix<T,Dynamic,Dynamic> scatter_;
  Matrix<T,Dynamic,1> mean_;
  T count_;

};

typedef NIW<double> NIWd;
typedef NIW<float> NIWf;


//  Copyright John Maddock 2012.
//  Use, modification and distribution are 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)
//
// Basic sanity check that header <boost/math/special_functions/bessel.hpp>
// #includes all the files that it needs to.
//
#include <boost/math/special_functions/jacobi_elliptic.hpp>
//
// Note this header includes no other headers, this is
// important if this test is to be meaningful:
//
#include "test_compile_result.hpp"

void compile_and_link_test()
{
   check_result<float>(boost::math::jacobi_elliptic<float>(f, f, static_cast<float*>(0), static_cast<float*>(0)));
   check_result<double>(boost::math::jacobi_elliptic<double>(d, d, static_cast<double*>(0), static_cast<double*>(0)));
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
   check_result<long double>(boost::math::jacobi_elliptic<long double>(l, l, static_cast<long double*>(0), static_cast<long double*>(0)));
#endif

   check_result<float>(boost::math::jacobi_sn<float>(f, f));
   check_result<double>(boost::math::jacobi_sn<double>(d, d));
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
   check_result<long double>(boost::math::jacobi_sn<long double>(l, l));
#endif

   check_result<float>(boost::math::jacobi_cn<float>(f, f));
   check_result<double>(boost::math::jacobi_cn<double>(d, d));
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
   check_result<long double>(boost::math::jacobi_cn<long double>(l, l));
#endif

   check_result<float>(boost::math::jacobi_dn<float>(f, f));
   check_result<double>(boost::math::jacobi_dn<double>(d, d));
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
   check_result<long double>(boost::math::jacobi_dn<long double>(l, l));
#endif

   check_result<float>(boost::math::jacobi_cd<float>(f, f));
   check_result<double>(boost::math::jacobi_cd<double>(d, d));
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
   check_result<long double>(boost::math::jacobi_cd<long double>(l, l));
#endif

   check_result<float>(boost::math::jacobi_dc<float>(f, f));
   check_result<double>(boost::math::jacobi_dc<double>(d, d));
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
   check_result<long double>(boost::math::jacobi_dc<long double>(l, l));
#endif

   check_result<float>(boost::math::jacobi_ns<float>(f, f));
   check_result<double>(boost::math::jacobi_ns<double>(d, d));
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
   check_result<long double>(boost::math::jacobi_ns<long double>(l, l));
#endif

   check_result<float>(boost::math::jacobi_sd<float>(f, f));
   check_result<double>(boost::math::jacobi_sd<double>(d, d));
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
   check_result<long double>(boost::math::jacobi_sd<long double>(l, l));
#endif

   check_result<float>(boost::math::jacobi_ds<float>(f, f));
   check_result<double>(boost::math::jacobi_ds<double>(d, d));
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
   check_result<long double>(boost::math::jacobi_ds<long double>(l, l));
#endif

   check_result<float>(boost::math::jacobi_nc<float>(f, f));
   check_result<double>(boost::math::jacobi_nc<double>(d, d));
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
   check_result<long double>(boost::math::jacobi_nc<long double>(l, l));
#endif

   check_result<float>(boost::math::jacobi_nd<float>(f, f));
   check_result<double>(boost::math::jacobi_nd<double>(d, d));
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
   check_result<long double>(boost::math::jacobi_nd<long double>(l, l));
#endif

   check_result<float>(boost::math::jacobi_sc<float>(f, f));
   check_result<double>(boost::math::jacobi_sc<double>(d, d));
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
   check_result<long double>(boost::math::jacobi_sc<long double>(l, l));
#endif

   check_result<float>(boost::math::jacobi_cs<float>(f, f));
   check_result<double>(boost::math::jacobi_cs<double>(d, d));
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
   check_result<long double>(boost::math::jacobi_cs<long double>(l, l));
#endif

}


#ifndef PHYSICS_HPP
#define PHYSICS_HPP

#define _USE_MATH_DEFINES
#include <vector>
#include <random>
#include <cstdint>
#include <Eigen/Core>

class physics
{
public:
	physics();
	virtual ~physics();

	void init(uint16_t obj_count);
	void deinit();
	void step(double delta_t);
	uint16_t get_obj_count(){return obj_count;}
	std::vector<Eigen::Vector3d>& get_pos(){return x[current];}
	std::vector<double> get_radii(){return r;}

private:
	/**
	 * @brief Finds acceleration due to gravity
	 * @param x_i Position
	 * @param skip_index The index of the object that acceleration is calc
	 * @return The acceleration vector
	 */
	Eigen::Vector3d accel(Eigen::Vector3d x_i, uint16_t skip_index);

	/**
	 * @brief Current and next indicies
	 */
	uint16_t current, next;
	uint16_t obj_count;
	double total_time;
	double mass_range[2];
	double radius_range[2];
	double distance_range[2];
	/**
	 * @brief Position (x1, x2, x3), two vectors for new and old
	 */
	std::vector<Eigen::Vector3d> x[2];
	/**
	 * @brief Velocity, two vectors for new and old
	 */
	std::vector<Eigen::Vector3d> v[2];
	/**
	 * @brief Acceleration, two vectors for new and old
	 */
	std::vector<Eigen::Vector3d> a[2];
	/**
	 * @brief Ocject radii
	 */
	std::vector<double> r;
	/**
	 * @brief Object mass
	 */
	std::vector<double> m;
	/**
	 * @brief A random generator that is initialized in the constructor
	 */
	std::mt19937_64 generator;

	double G = 6.67408e-11;
};

#endif


///////////////////////////////////////////////////////////////
//  Copyright 2011 John Maddock. 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_

   //[cpp_dec_float_eg
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/math/special_functions/gamma.hpp>
#include <iostream>

int main()
{
   using namespace boost::multiprecision;

   // Operations at fixed precision and full numeric_limits support:
   cpp_dec_float_100 b = 2;
   std::cout << std::numeric_limits<cpp_dec_float_100>::digits << std::endl;
   // Note that digits10 is the same as digits, since we're base 10! :
   std::cout << std::numeric_limits<cpp_dec_float_100>::digits10 << std::endl;
   // We can use any C++ std lib function, lets print all the digits as well:
   std::cout << std::setprecision(std::numeric_limits<cpp_dec_float_100>::max_digits10)
      << log(b) << std::endl; // print log(2)
   // We can also use any function from Boost.Math:
   std::cout << boost::math::tgamma(b) << std::endl;
   // These even work when the argument is an expression template:
   std::cout << boost::math::tgamma(b * b) << std::endl;
   // And since we have an extended exponent range we can generate some really large
   // numbers here (4.0238726007709377354370243e+2564):
   std::cout << boost::math::tgamma(cpp_dec_float_100(1000)) << std::endl;
   return 0;
}
//]


//=======================================================================
// Copyright (c)
//
// 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)
//=======================================================================
/**
 * @files zelikovsky_11_per_6_long_test.cpp
 * @brief
 * @author Piotr Wygocki
 * @version 1.0
 * @date 2013-02-04
 */

#include <boost/test/unit_test.hpp>

// this include must be here! //hack for clang
#include "paal/steiner_tree/zelikovsky_11_per_6.hpp"

#include "test_utils/logger.hpp"
#include "test_utils/read_steinlib.hpp"
#include "test_utils/test_result_check.hpp"

#include "paal/utils/irange.hpp"
#include "paal/data_structures/bimap.hpp"

#include <vector>

BOOST_AUTO_TEST_CASE(zelikovsky_11_per_6_test) {
    std::vector<paal::steiner_tree_test_with_metric> data;
    LOGLN("READING INPUT...");
    read_steinlib_tests(data);
    for (auto const &test : data) {
        LOGLN("TEST " << test.test_name);
        LOGLN("OPT " << test.optimal);

        using Metric = decltype(test.metric);
        using voronoiT = paal::data_structures::voronoi<Metric>;
        using FSet = typename voronoiT::GeneratorsSet;
        voronoiT voronoi(
            FSet(test.terminals.begin(), test.terminals.end()),
            FSet(test.steiner_points.begin(), test.steiner_points.end()),
            test.metric);
        std::vector<int> selected_steiner_points;
        paal::steiner_tree_zelikovsky11per6approximation(
            test.metric, voronoi, std::back_inserter(selected_steiner_points));

        auto res_range = boost::join(test.terminals, selected_steiner_points);
        LOG_COPY_RANGE_DEL(res_range, ",");
        paal::data_structures::bimap<int> idx;
        auto g = paal::data_structures::metric_to_bgl_with_index(
            test.metric, res_range, idx);
        std::vector<int> pm(res_range.size());
        boost::prim_minimum_spanning_tree(g, &pm[0]);
        auto idx_m = paal::data_structures::make_metric_on_idx(test.metric, idx);
        int res(0);
        for (int i : paal::irange(pm.size())) {
            if (pm[i] != i) {
                res += idx_m(i, pm[i]);
            }
        }
        check_result(res, test.optimal, 11. / 6);
    }
}


//  Copyright (c) 2019 AUTHORS
//
//  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)

// Original Fortran source: http://cococubed.asu.edu/research_pages/sedov.shtml

/* sedov3.f -- translated by f2c (version 20160102).
 You must link the resulting object file with libf2c:
 on Microsoft Windows system, link with libf2c.lib;
 on Linux or Unix systems, link with .../path/to/libf2c.a -lm
 or, if you install libf2c.a in a standard place, with -lf2c -lm
 -- in that order, at the end of the command line, as ina
 cc *.o -lf2c -lm
 Source for libf2c is in /netlib/f2c/libf2c.zip, e.g.,

 http://www.netlib.org/f2c/libf2c.zip
 */

#include <cmath>
#include <vector>
#include <unordered_map>
#include <memory>
//#include "f2c.h"
#include <memory>
#include <assert.h>
#if !defined(OCTOTIGER_HAVE_BOOST_MULTIPRECISION)
#include <quadmath.h>
using sed_real = __float128;

sed_real _exp(sed_real a) {
    return expq(a);
}

sed_real pow_dd(sed_real *a, sed_real *b) {
    return powq(*a, *b);
}

sed_real d_sign(sed_real *a, sed_real * b) {
    return copysignq(*a, *b);
}
#else
#include <boost/multiprecision/cpp_bin_float.hpp>
using sed_real = boost::multiprecision::cpp_bin_float_quad;

sed_real _exp(sed_real a) {
    return boost::multiprecision::exp(a);
}

sed_real pow_dd(sed_real *a, sed_real *b) {
    return boost::multiprecision::pow(*a, *b);
}

sed_real d_sign(sed_real *a, sed_real * b) {
    if ((*a > static_cast<sed_real>(0.) && (*b > static_cast<sed_real>(0.))) ||
        (*a < static_cast<sed_real>(0.) && (*b < static_cast<sed_real>(0.))))
    {
        return *a;
    }
    return *b;
}
#endif

/* Subroutine */int sed_1d__(sed_real *time, int *nstep,
        sed_real * xpos, sed_real *eblast, sed_real *omega_in__,
        sed_real * xgeom_in__, sed_real *rho0, sed_real *vel0,
        sed_real *ener0, sed_real *pres0, sed_real *cs0, sed_real *gam0,
        sed_real *den, sed_real *ener, sed_real *pres, sed_real *vel,
        sed_real *cs);
/* Common Block Declarations */

struct {
    sed_real gamma, gamm1, gamp1, gpogm, xgeom, xg2, rwant, r2, a0, a1, a2, a3,
            a4, a5, a_val__, b_val__, c_val__, d_val__, e_val__, omega, vv,
            xlam_want__, vwant, rvv;
    bool lsingular, lstandard, lvacuum, lomega2, lomega3;
} slap_;

#define slap_1 slap_

struct {
    sed_real gam_int__;
} cmidp_;

#define cmidp_1 cmidp_

/* Table of constant values */

using D_fp = sed_real (*)(sed_real*);
using S_fp = int (*)(
    D_fp, sed_real*, sed_real*, sed_real*, int*);    //Subroutine
using U_fp = int (*)();                              // Unknown procedure type

static sed_real c_b52 = 2.;
static sed_real c_b53 = 1e-10;
static sed_real c_b79 = 0.f;
static sed_real c_b80 = 1e-30;
static int c__3 = 3;
static int c__5 = 5;
static sed_real efun01_(sed_real *v);
static sed_real efun02_(sed_real *v);
static sed_real sed_v_find__(sed_real *v);
static sed_real sed_r_find__(sed_real *r__);
/* Subroutine */int sedov_funcs__(sed_real *v, sed_real *l_fun__, sed_real *dlamdv,
        sed_real *f_fun__, sed_real *g_fun__, sed_real *h_fun__);
/* Subroutine */static int midpnt_(D_fp func, sed_real *a, sed_real *b, sed_real *s,
        int *n);
/* Subroutine */static int midpowl_(D_fp funk, sed_real *aa, sed_real *bb,
        sed_real *s, int *n);
/* Subroutine */static int midpowl2_(D_fp funk, sed_real *aa, sed_real *bb,
        sed_real *s, int *n);
/* Subroutine */static int qromo_(D_fp func, sed_real *a, sed_real *b, sed_real *eps,
        sed_real *ss, S_fp choose);
/* Subroutine */static int polint_(sed_real *xa, sed_real *ya, int *n,
        sed_real *x, sed_real *y, sed_real *dy);
static sed_real zeroin_(sed_real *ax, sed_real *bx, D_fp f, sed_real *tol);

int pow_ii(int * a, int * b) {
    return std::pow(*a, *b);
}

/* Subroutine */int sed_1d__(sed_real *time, int *nstep, sed_real * xpos,
        sed_real *eblast, sed_real *omega_in__, sed_real * xgeom_in__, sed_real *rho0,
        sed_real *vel0, sed_real *ener0, sed_real *pres0, sed_real *cs0, sed_real *gam0,
        sed_real *den, sed_real *ener, sed_real *pres, sed_real *vel, sed_real *cs) {

    /* System generated locals */
    int i__1;
    sed_real d__1, d__2, d__3;

    /* Builtin functions */

    /* Local variables */
    static int i__;
    static sed_real p2, v0, u2, v2, us;
    static sed_real vat, rho1, rho2;
    static sed_real vmin, eval1, eval2, alpha, f_fun__;
    static sed_real g_fun__, h_fun__, l_fun__;
    static sed_real vstar, denom2, denom3, dlamdv;
    sed_real zeroin_(sed_real *ax, sed_real *bx, D_fp f, sed_real *tol);

    /* ..this routine produces 1d solutions for a sedov blast wave propagating */
    /* ..through a density gradient rho = rho**(-omega) */
    /* ..in planar, cylindrical or spherical geometry */
    /* ..for the standard, singular and vaccum cases. */
    /* ..standard case: a nonzero solution extends from the shock to the origin, */
    /* ..               where the pressure is finite. */
    /* ..singular case: a nonzero solution extends from the shock to the origin, */
    /* ..               where the pressure vanishes. */
    /* ..vacuum case  : a nonzero solution extends from the shock to a boundary point, */
    /* ..               where the density vanishes making the pressure meaningless. */
    /* ..input: */
    /* ..time     = temporal point where solution is desired seconds */
    /* ..xpos(i)  = spatial points where solution is desired cm */
    /* ..eblast   = energy of blast erg */
    /* ..rho0     = ambient density g/cm**3    rho = rho0 * r**(-omega_in) */
    /* ..omegain  = density power law _exponent rho = rho0 * r**(-omega_in) */
    /* ..vel0     = ambient material speed cm/s */
    /* ..pres0    = ambient pressure erg/cm**3 */
    /* ..cs0      = ambient sound speed cm/s */
    /* ..gam0   = gamma law equation of state */
    /* ..xgeom_in = geometry factor, 3=spherical, 2=cylindircal, 1=planar */
    /* ..for efficiency reasons (doing the energy integrals only once), */
    /* ..this routine returns the solution for an array of spatial points */
    /* ..at the desired time point. */
    /* ..output: */
    /* ..den(i)  = density  g/cm**3 */
    /* ..ener(i) = specific internal energy erg/g */
    /* ..pres(i) = presssure erg/cm**3 */
    /* ..vel(i)  = velocity cm/s */
    /* ..cs(i)   = sound speed cm/s */
    /* ..this routine is based upon two papers: */
    /* .."evaluation of the sedov-von neumann-taylor blast wave solution" */
    /* ..jim kamm, la-ur-00-6055 */
    /* .."the sedov self-similiar point blast solutions in nonuniform media" */
    /* ..david book, shock waves, 4, 1, 1994 */
    /* ..although the ordinary differential equations are analytic, */
    /* ..the sedov _expressions appear to become singular for various */
    /* ..combinations of parameters and at the lower limits of the integration */
    /* ..range. all these singularies are removable and done so by this routine. */
    /* ..these routines are written in sed_real*8 precision because the */
    /* ..sed_real*8 implementations simply run out of precision "near" the origin */
    /* ..in the standard case or the transition region in the vacuum case. */
    /* ..declare the pass */
    /* ..local variables */
    /* ..eps controls the integration accuracy, don't get too greedy or the number */
    /* ..of function evaluations required kills. */
    /* ..eps2 controls the root find accuracy */
    /* ..osmall controls the size of transition regions */
    /* ..common block communication */
    /* ..common block communication with the integration stepper */
    /* ..popular formats */
    /* ..initialize the solution */
    /* Parameter adjustments */
    --cs;
    --vel;
    --pres;
    --ener;
    --den;
    --xpos;

    /* Function Body */
    /* L87: */
    /* L88: */
    i__1 = *nstep;
    for (i__ = 1; i__ <= i__1; ++i__) {
        den[i__] = 0.f;
        vel[i__] = 0.f;
        pres[i__] = 0.f;
        ener[i__] = 0.f;
        cs[i__] = 0.f;
    }
    /* ..return on unphysical cases */
    /* ..infinite mass */
    if (*omega_in__ >= *xgeom_in__) {
        return 0;
    }
    /* ..transfer the pass to common block and create some frequent combinations */
    slap_1.gamma = *gam0;
    slap_1.gamm1 = slap_1.gamma - 1.f;
    slap_1.gamp1 = slap_1.gamma + 1.f;
    slap_1.gpogm = slap_1.gamp1 / slap_1.gamm1;
    slap_1.xgeom = *xgeom_in__;
    slap_1.omega = *omega_in__;
    slap_1.xg2 = slap_1.xgeom + 2.f - slap_1.omega;
    denom2 = slap_1.gamm1 * 2.f + slap_1.xgeom - slap_1.gamma * slap_1.omega;
    denom3 = slap_1.xgeom * (2.f - slap_1.gamma) - slap_1.omega;
    /* ..post shock location v2 and location of singular point vstar */
    /* ..kamm equation 18 and 19 */
    v2 = 4.f / (slap_1.xg2 * slap_1.gamp1);
    vstar = 2.f / (slap_1.gamm1 * slap_1.xgeom + 2.f);
    /* ..set two bools that determines the type of solution */
    slap_1.lstandard = false;
    slap_1.lsingular = false;
    slap_1.lvacuum = false;
    if ((d__1 = v2 - vstar, fabs(static_cast<double>(d__1))) <= 1e-4) {
        slap_1.lsingular = true;
    } else if (v2 < vstar - 1e-4) {
        slap_1.lstandard = true;
    } else if (v2 > vstar + 1e-4) {
        slap_1.lvacuum = true;
    }
    /* ..two apparent singularies, book's notation for omega2 and omega3 */
    slap_1.lomega2 = false;
    slap_1.lomega3 = false;
    if (fabs(static_cast<double>(denom2)) <= 1e-4) {
        slap_1.lomega2 = true;
        denom2 = 1e-8f;
    } else if (fabs(static_cast<double>(denom3)) <= 1e-4) {
        slap_1.lomega3 = true;
        denom3 = 1e-8f;
    }
    /* ..various _exponents, kamm equations 42-47 */
    /* ..in terms of book's notation: */
    /* ..a0=beta6 a1=beta1  a2=-beta2 a3=beta3 a4=beta4 a5=-beta5 */
    slap_1.a0 = 2.f / slap_1.xg2;
    slap_1.a2 = -slap_1.gamm1 / denom2;
    slap_1.a1 = slap_1.xg2 * slap_1.gamma / (slap_1.xgeom * slap_1.gamm1 + 2.f)
            * ((slap_1.xgeom * (2.f - slap_1.gamma) - slap_1.omega) * 2.f
                    / (slap_1.gamma * slap_1.xg2 * slap_1.xg2) - slap_1.a2);
    slap_1.a3 = (slap_1.xgeom - slap_1.omega) / denom2;
    slap_1.a4 = slap_1.xg2 * (slap_1.xgeom - slap_1.omega) * slap_1.a1 / denom3;
    slap_1.a5 = (slap_1.omega * slap_1.gamp1 - slap_1.xgeom * 2.f) / denom3;
    /* ..frequent combinations, kamm equations 33-37 */
    slap_1.a_val__ = slap_1.xg2 * .25f * slap_1.gamp1;
    slap_1.b_val__ = slap_1.gpogm;
    slap_1.c_val__ = slap_1.xg2 * .5f * slap_1.gamma;
    slap_1.d_val__ = slap_1.xg2 * slap_1.gamp1
            / (slap_1.xg2 * slap_1.gamp1
                    - (slap_1.xgeom * slap_1.gamm1 + 2.f) * 2.f);
    slap_1.e_val__ = (slap_1.xgeom * slap_1.gamm1 + 2.f) * .5f;
    /* ..evaluate the energy integrals */
    /* ..the singular case can be done by hand; save some cpu cycles */
    /* ..kamm equations 80, 81, and 85 */
    if (slap_1.lsingular) {
        /* Computing 2nd power */
        d__1 = slap_1.gamm1 * slap_1.xgeom + 2.f;
        eval2 = slap_1.gamp1 / (slap_1.xgeom * (d__1 * d__1));
        eval1 = 2.f / slap_1.gamm1 * eval2;
        /* Computing 2nd power */
        d__1 = slap_1.gamm1 * slap_1.xgeom + 2.f;
        alpha = slap_1.gpogm * pow_dd(&c_b52, &slap_1.xgeom)
                / (slap_1.xgeom * (d__1 * d__1));
        if (static_cast<int>(slap_1.xgeom) != 1) {
            alpha *= 3.1415926535897932384626433832795029;
        }
        /* ..for the standard or vacuum cases */
        /* ..v0 = post-shock origin v0 and vv = vacuum boundary vv */
        /* ..set the radius corespondin to vv to zero for now */
        /* ..kamm equations 18, and 28. */
    } else {
        v0 = 2.f / (slap_1.xg2 * slap_1.gamma);
        slap_1.vv = 2.f / slap_1.xg2;
        slap_1.rvv = 0.;
        if (slap_1.lstandard) {
            vmin = v0;
        }
        if (slap_1.lvacuum) {
            vmin = slap_1.vv;
        }
        /* ..the first energy integral */
        /* ..in the standard case the term (c_val*v - 1) might be singular at v=vmin */
        if (slap_1.lstandard) {
            cmidp_1.gam_int__ = slap_1.a3 - slap_1.a2 * slap_1.xg2 - 1.f;
            if (cmidp_1.gam_int__ >= 0.) {
                qromo_(static_cast<D_fp>(efun01_), &vmin, &v2, &c_b53, &eval1,
                        static_cast<S_fp>(midpnt_));
            } else {
                cmidp_1.gam_int__ = fabs(static_cast<double>(cmidp_1.gam_int__));
                qromo_(static_cast<D_fp>(efun01_), &vmin, &v2, &c_b53, &eval1,
                        static_cast<S_fp>(midpowl_));
            }
            /* ..in the vacuum case the term (1 - c_val/gamma*v) might be singular at v=vmin */
        } else if (slap_1.lvacuum) {
            cmidp_1.gam_int__ = slap_1.a5;
            if (cmidp_1.gam_int__ >= 0.) {
                qromo_(static_cast<D_fp>(efun01_), &vmin, &v2, &c_b53, &eval1,
                        static_cast<S_fp>(midpnt_));
            } else {
                cmidp_1.gam_int__ = fabs(static_cast<double>(cmidp_1.gam_int__));
                qromo_(static_cast<D_fp>(efun01_), &vmin, &v2, &c_b53, &eval1,
                        static_cast<S_fp>(midpowl2_));
            }
        }
        /* ..the second energy integral */
        /* ..in the standard case the term (c_val*v - 1) might be singular at v=vmin */
        if (slap_1.lstandard) {
            cmidp_1.gam_int__ = slap_1.a3 - slap_1.a2 * slap_1.xg2 - 2.f;
            if (cmidp_1.gam_int__ >= 0.) {
                qromo_(static_cast<D_fp>(efun02_), &vmin, &v2, &c_b53, &eval2,
                        static_cast<S_fp>(midpnt_));
            } else {
                cmidp_1.gam_int__ = fabs(static_cast<double>(cmidp_1.gam_int__));
                qromo_(static_cast<D_fp>(efun02_), &vmin, &v2, &c_b53, &eval2,
                        static_cast<S_fp>(midpowl_));
            }
            /* ..in the vacuum case the term (1 - c_val/gamma*v) might be singular at v=vmin */
        } else if (slap_1.lvacuum) {
            cmidp_1.gam_int__ = slap_1.a5;
            if (cmidp_1.gam_int__ >= 0.) {
                qromo_(static_cast<D_fp>(efun02_), &vmin, &v2, &c_b53, &eval2,
                        static_cast<S_fp>(midpnt_));
            } else {
                cmidp_1.gam_int__ = fabs(static_cast<double>(cmidp_1.gam_int__));
                qromo_(static_cast<D_fp>(efun02_), &vmin, &v2, &c_b53, &eval2,
                        static_cast<S_fp>(midpowl2_));
            }
        }
        /* ..kamm equations 57 and 58 for alpha, in a slightly different form. */
        if (static_cast<int>(slap_1.xgeom) == 1) {
            alpha = eval1 * .5f + eval2 / slap_1.gamm1;
        } else {
            alpha = (slap_1.xgeom - 1.f) * 3.1415926535897932384626433832795029
                    * (eval1 + eval2 * 2.f / slap_1.gamm1);
        }
    }
    /* ..write what we have for the energy integrals */
    if (true) {
//		s_wsfe(&io___42);
//		do_fio(&c__1, "xgeom =", (ftnlen) 7);
//		do_fio(&c__1, (char *) &slap_1.xgeom, (ftnlen) sizeof(sed_real));
//		do_fio(&c__1, "eblast=", (ftnlen) 7);
//		do_fio(&c__1, (char *) &(*eblast), (ftnlen) sizeof(sed_real));
//		do_fio(&c__1, "omega =", (ftnlen) 7);
//		do_fio(&c__1, (char *) &slap_1.omega, (ftnlen) sizeof(sed_real));
//		do_fio(&c__1, "alpha =", (ftnlen) 7);
//		do_fio(&c__1, (char *) &alpha, (ftnlen) sizeof(sed_real));
//		do_fio(&c__1, "j1    =", (ftnlen) 7);
//		do_fio(&c__1, (char *) &eval1, (ftnlen) sizeof(sed_real));
//		do_fio(&c__1, "j2    =", (ftnlen) 7);
//		do_fio(&c__1, (char *) &eval2, (ftnlen) sizeof(sed_real));
//		e_wsfe();
    }
    /*      write(6,87) omega,alpha */
    /* ..immediate post-shock values */
    /* ..kamm page 14 or equations 14, 16, 5, 13 */
    /* ..r2 = shock position, u2 = shock speed, rho1 = pre-shock density, */
    /* ..u2 = post-shock material speed, rho2 = post-shock density, */
    /* ..p2 = post-shock pressure, e2 = post-shoock specific internal energy, */
    /* ..and cs2 = post-shock sound speed */
    d__1 = *eblast / (alpha * *rho0);
    d__2 = 1.f / slap_1.xg2;
    d__3 = 2.f / slap_1.xg2;
    slap_1.r2 = pow_dd(&d__1, &d__2) * pow_dd(time, &d__3);
    us = 2.f / slap_1.xg2 * slap_1.r2 / *time;
    d__1 = -slap_1.omega;
    rho1 = *rho0 * pow_dd(&slap_1.r2, &d__1);
    u2 = us * 2.f / slap_1.gamp1;
    rho2 = slap_1.gpogm * rho1;
    /* Computing 2nd power */
    d__1 = us;
    p2 = rho1 * 2.f * (d__1 * d__1) / slap_1.gamp1;
//	e2 = p2 / (slap_1.gamm1 * rho2);
//	cs2 = sqrt(slap_1.gamma * p2 / rho2);
    /* ..find the radius corresponding to vv */
    if (slap_1.lvacuum) {
        slap_1.vwant = slap_1.vv;
        slap_1.rvv = zeroin_(&c_b79, &slap_1.r2, static_cast<D_fp>(sed_r_find__), &c_b80);
    }
//	if (slap_1.lstandard) {
//		s_wsfe(&io___50);
//		do_fio(&c__1, "r2    =", (ftnlen) 7);
//		do_fio(&c__1, (char *) &slap_1.r2, (ftnlen) sizeof(sed_real));
//		do_fio(&c__1, "rho2  =", (ftnlen) 7);
//		do_fio(&c__1, (char *) &rho2, (ftnlen) sizeof(sed_real));
//		do_fio(&c__1, "u2    =", (ftnlen) 7);
//		do_fio(&c__1, (char *) &u2, (ftnlen) sizeof(sed_real));
//		do_fio(&c__1, "e2    =", (ftnlen) 7);
//		do_fio(&c__1, (char *) &e2, (ftnlen) sizeof(sed_real));
//		do_fio(&c__1, "p2    =", (ftnlen) 7);
//		do_fio(&c__1, (char *) &p2, (ftnlen) sizeof(sed_real));
//		do_fio(&c__1, "cs2   =", (ftnlen) 7);
//		do_fio(&c__1, (char *) &cs2, (ftnlen) sizeof(sed_real));
//		e_wsfe();
//	}
//	if (slap_1.lvacuum) {
//		s_wsfe(&io___51);
//		do_fio(&c__1, "rv    =", (ftnlen) 7);
//		do_fio(&c__1, (char *) &slap_1.rvv, (ftnlen) sizeof(sed_real));
//		do_fio(&c__1, "r2    =", (ftnlen) 7);
//		do_fio(&c__1, (char *) &slap_1.r2, (ftnlen) sizeof(sed_real));
//		do_fio(&c__1, "rho2  =", (ftnlen) 7);
//		do_fio(&c__1, (char *) &rho2, (ftnlen) sizeof(sed_real));
//		do_fio(&c__1, "u2    =", (ftnlen) 7);
//		do_fio(&c__1, (char *) &u2, (ftnlen) sizeof(sed_real));
//		do_fio(&c__1, "e2    =", (ftnlen) 7);
//		do_fio(&c__1, (char *) &e2, (ftnlen) sizeof(sed_real));
//		do_fio(&c__1, "p2    =", (ftnlen) 7);
//		do_fio(&c__1, (char *) &p2, (ftnlen) sizeof(sed_real));
//		do_fio(&c__1, "cs2   =", (ftnlen) 7);
//		do_fio(&c__1, (char *) &cs2, (ftnlen) sizeof(sed_real));
//		e_wsfe();
//	}
    /* ..now start the loop over spatial positions */
    i__1 = *nstep;
    for (i__ = 1; i__ <= i__1; ++i__) {
        slap_1.rwant = xpos[i__];
        /* ..if we are upstream from the shock front */
        if (slap_1.rwant > slap_1.r2) {
            d__1 = -slap_1.omega;
            den[i__] = *rho0 * pow_dd(&slap_1.rwant, &d__1);
            vel[i__] = *vel0;
            pres[i__] = *pres0;
            ener[i__] = *ener0;
            cs[i__] = *cs0;
            /* ..if we are between the origin and the shock front */
            /* ..find the correct similarity value for this radius in the standard or vacuum cases */
        } else {
            if (slap_1.lstandard) {
                d__1 = v0 * .9f;
                vat = zeroin_(&d__1, &v2, static_cast<D_fp>(sed_v_find__), &c_b80);
            } else if (slap_1.lvacuum) {
                d__1 = slap_1.vv * 1.2f;
                vat = zeroin_(&v2, &d__1, static_cast<D_fp>(sed_v_find__), &c_b80);
            }
            /* ..the physical solution */
            sedov_funcs__(&vat, &l_fun__, &dlamdv, &f_fun__, &g_fun__,
                    &h_fun__);
            den[i__] = rho2 * g_fun__;
            vel[i__] = u2 * f_fun__;
            pres[i__] = p2 * h_fun__;
            ener[i__] = 0.f;
            cs[i__] = 0.f;
            if (den[i__] != 0.f) {
                ener[i__] = pres[i__] / (slap_1.gamm1 * den[i__]);
                cs[i__] = sqrt(static_cast<double>(slap_1.gamma * pres[i__] / den[i__]));
            }
        }
        /* ..end of loop over positions */
    }
    return 0;
} /* sed_1d__ */

sed_real efun01_(sed_real *v) {
    /* System generated locals */
    sed_real ret_val, d__1, d__2;
    ret_val = 0;

    /* Builtin functions */
    sed_real pow_dd(sed_real *, sed_real *);

    /* Local variables */
    static sed_real f_fun__, g_fun__, h_fun__, l_fun__, dlamdv;

    /* ..evaluates the first energy integrand, kamm equations 67 and 10. */
    /* ..the (c_val*v - 1) term might be singular at v=vmin in the standard case. */
    /* ..the (1 - c_val/gamma * v) term might be singular at v=vmin in the vacuum case. */
    /* ..due care should be taken for these removable singularities by the integrator. */
    /* ..declare the pass */
    /* ..common block communication */
    /* ..local variables */
    /* ..go */
    sedov_funcs__(v, &l_fun__, &dlamdv, &f_fun__, &g_fun__, &h_fun__);
    d__1 = slap_1.xgeom + 1.f;
    /* Computing 2nd power */
    d__2 = *v;
    ret_val = dlamdv * pow_dd(&l_fun__, &d__1) * slap_1.gpogm * g_fun__
            * (d__2 * d__2);
    return ret_val;
} /* efun01_ */

sed_real efun02_(sed_real *v) {
    /* System generated locals */
    sed_real ret_val, d__1;
    ret_val = 0;

    /* Builtin functions */
    sed_real pow_dd(sed_real *, sed_real *);

    /* Local variables */
    static sed_real z__;
    static sed_real f_fun__, g_fun__, h_fun__, l_fun__, dlamdv;

    /* ..evaluates the second energy integrand, kamm equations 68 and 11. */
    /* ..the (c_val*v - 1) term might be singular at v=vmin in the standard case. */
    /* ..the (1 - c_val/gamma * v) term might be singular at v=vmin in the vacuum case. */
    /* ..due care should be taken for these removable singularities by the integrator. */
    /* ..declare the pass */
    /* ..common block communication */
    /* ..local variables */
    /* ..go */
    sedov_funcs__(v, &l_fun__, &dlamdv, &f_fun__, &g_fun__, &h_fun__);
    /* Computing 2nd power */
    d__1 = slap_1.xgeom + 2.f - slap_1.omega;
    z__ = 8.f / (d__1 * d__1 * slap_1.gamp1);
    d__1 = slap_1.xgeom - 1.f;
    ret_val = dlamdv * pow_dd(&l_fun__, &d__1) * h_fun__ * z__;
    return ret_val;
} /* efun02_ */

sed_real sed_v_find__(sed_real *v) {
    /* System generated locals */
    sed_real ret_val;
    ret_val = 0;

    /* Local variables */
    static sed_real f_fun__, g_fun__, h_fun__, l_fun__, dlamdv;

    /* ..given corresponding physical distances, find the similarity variable v */
    /* ..kamm equation 38 as a root find */
    /* ..declare the pass */
    /* ..common block communication */
    /* ..local variables */
    sedov_funcs__(v, &l_fun__, &dlamdv, &f_fun__, &g_fun__, &h_fun__);
    ret_val = slap_1.r2 * l_fun__ - slap_1.rwant;
    return ret_val;
} /* sed_v_find__ */

sed_real sed_r_find__(sed_real *r__) {
    /* System generated locals */
    sed_real ret_val;
    ret_val = 0;

    /* Local variables */
    static sed_real f_fun__, g_fun__, h_fun__, l_fun__, dlamdv;

    /* ..given the similarity variable v, find the corresponding physical distance */
    /* ..kamm equation 38 as a root find */
    /* ..declare the pass */
    /* ..common block communication */
    /* ..local variables */
    sedov_funcs__(&slap_1.vwant, &l_fun__, &dlamdv, &f_fun__, &g_fun__,
            &h_fun__);
    ret_val = slap_1.r2 * l_fun__ - *r__;
    return ret_val;
} /* sed_r_find__ */

/* Subroutine */int sedov_funcs__(sed_real *v, sed_real *l_fun__, sed_real *dlamdv,
        sed_real *f_fun__, sed_real *g_fun__, sed_real *h_fun__) {
    /* System generated locals */
    sed_real d__1, d__2, d__3;

    /* Builtin functions */
//	sed_real pow_dd(sed_real *, sed_real *), _exp(sed_real);

    /* Local variables */
    static sed_real y, z__, c2, c6, x1, x2, x3, x4, pp1, pp2, pp3, pp4, cbag,
            ebag, beta0, dx1dv, dx2dv, dx3dv, dx4dv, dpp2dv;

    /* ..given the similarity variable v, returns functions */
    /* ..lambda, f, g, and h and the derivative of lambda with v dlamdv */
    /* ..although the ordinary differential equations are analytic, */
    /* ..the sedov _expressions appear to become singular for various */
    /* ..combinations of parameters and at the lower limits of the integration */
    /* ..range. all these singularies are removable and done so by this routine. */
    /* ..declare the pass */
    /* ..common block communication */
    /* ..local variables */
    /* ..frequent combinations and their derivative with v */
    /* ..kamm equation 29-32, x4 a bit different to save a divide */
    /* ..x1 is book's F */
    x1 = slap_1.a_val__ * *v;
    dx1dv = slap_1.a_val__;
    /* Computing MAX */
    d__1 = 1e-30, d__2 = slap_1.c_val__ * *v - 1.f;
    cbag = fmax(static_cast<double>(d__1), static_cast<double>(d__2));
    x2 = slap_1.b_val__ * cbag;
    dx2dv = slap_1.b_val__ * slap_1.c_val__;
    ebag = 1.f - slap_1.e_val__ * *v;
    x3 = slap_1.d_val__ * ebag;
    dx3dv = -slap_1.d_val__ * slap_1.e_val__;
    x4 = slap_1.b_val__ * (1.f - slap_1.xg2 * .5f * *v);
    dx4dv = -slap_1.b_val__ * .5f * slap_1.xg2;
    /* ..transition region between standard and vacuum cases */
    /* ..kamm page 15 or equations 88-92 */
    /* ..lambda = l_fun is book's zeta */
    /* ..f_fun is books V, g_fun is book's D, h_fun is book's P */
    if (slap_1.lsingular) {
        *l_fun__ = slap_1.rwant / slap_1.r2;
        *dlamdv = 0.f;
        *f_fun__ = *l_fun__;
        d__1 = slap_1.xgeom - 2.f;
        *g_fun__ = pow_dd(l_fun__, &d__1);
        *h_fun__ = pow_dd(l_fun__, &slap_1.xgeom);
        /* ..for the vacuum case in the hole */
    } else if (slap_1.lvacuum && slap_1.rwant < slap_1.rvv) {
        *l_fun__ = 0.f;
        *dlamdv = 0.f;
        *f_fun__ = 0.f;
        *g_fun__ = 0.f;
        *h_fun__ = 0.f;
        /* ..omega = omega2 = (2*(gamma -1) + xgeom)/gamma case, denom2 = 0 */
        /* ..book _expressions 20-22 */
    } else if (slap_1.lomega2) {
        beta0 = 1.f / (slap_1.e_val__ * 2.f);
        pp1 = slap_1.gamm1 * beta0;
        c6 = slap_1.gamp1 * .5f;
        c2 = c6 / slap_1.gamma;
        y = 1.f / (x1 - c2);
        z__ = (1.f - x1) * y;
        pp2 = slap_1.gamp1 * beta0 * z__;
        dpp2dv = -slap_1.gamp1 * beta0 * dx1dv * y * (z__ + 1.f);
        pp3 = (4.f - slap_1.xgeom - slap_1.gamma * 2.f) * beta0;
        pp4 = -slap_1.xgeom * slap_1.gamma * beta0;
        d__1 = -slap_1.a0;
        *l_fun__ = pow_dd(&x1, &d__1) * pow_dd(&x2, &pp1) * _exp(pp2);
        *dlamdv = (-slap_1.a0 * dx1dv / x1 + pp1 * dx2dv / x2 + dpp2dv)
                * *l_fun__;
        *f_fun__ = x1 * *l_fun__;
        d__1 = slap_1.a0 * slap_1.omega;
        *g_fun__ = pow_dd(&x1, &d__1) * pow_dd(&x2, &pp3) * pow_dd(&x4, &
        slap_1.a5) * _exp(pp2 * -2.f);
        d__1 = slap_1.a0 * slap_1.xgeom;
        d__2 = slap_1.a5 + 1.f;
        *h_fun__ = pow_dd(&x1, &d__1) * pow_dd(&x2, &pp4) * pow_dd(&x4, &d__2);
        /* ..omega = omega3 = xgeom*(2 - gamma) case, denom3 = 0 */
        /* ..book _expressions 23-25 */
    } else if (slap_1.lomega3) {
        beta0 = 1.f / (slap_1.e_val__ * 2.f);
        pp1 = slap_1.a3 + slap_1.omega * slap_1.a2;
        pp2 = 1.f - beta0 * 4.f;
        c6 = slap_1.gamp1 * .5f;
        pp3 = -slap_1.xgeom * slap_1.gamma * slap_1.gamp1 * beta0 * (1.f - x1)
                / (c6 - x1);
        pp4 = (slap_1.xgeom * slap_1.gamm1 - slap_1.gamma) * 2.f * beta0;
        d__1 = -slap_1.a0;
        d__2 = -slap_1.a2;
        d__3 = -slap_1.a1;
        *l_fun__ = pow_dd(&x1, &d__1) * pow_dd(&x2, &d__2) * pow_dd(&x4, &d__3);
        *dlamdv = -(slap_1.a0 * dx1dv / x1 + slap_1.a2 * dx2dv / x2 +
        slap_1.a1 * dx4dv / x4) * *l_fun__;
        *f_fun__ = x1 * *l_fun__;
        d__1 = slap_1.a0 * slap_1.omega;
        *g_fun__ = pow_dd(&x1, &d__1) * pow_dd(&x2, &pp1) * pow_dd(&x4, &pp2)
                * _exp(pp3);
        d__1 = slap_1.a0 * slap_1.xgeom;
        *h_fun__ = pow_dd(&x1, &d__1) * pow_dd(&x4, &pp4) * _exp(pp3);
        /* ..for the standard or vacuum case not in the hole */
        /* ..kamm equations 38-41 */
    } else {
        d__1 = -slap_1.a0;
        d__2 = -slap_1.a2;
        d__3 = -slap_1.a1;
        *l_fun__ = pow_dd(&x1, &d__1) * pow_dd(&x2, &d__2) * pow_dd(&x3, &d__3);
        *dlamdv = -(slap_1.a0 * dx1dv / x1 + slap_1.a2 * dx2dv / x2 +
        slap_1.a1 * dx3dv / x3) * *l_fun__;
        *f_fun__ = x1 * *l_fun__;
        d__1 = slap_1.a0 * slap_1.omega;
        d__2 = slap_1.a3 + slap_1.a2 * slap_1.omega;
        d__3 = slap_1.a4 + slap_1.a1 * slap_1.omega;
        *g_fun__ = pow_dd(&x1, &d__1) * pow_dd(&x2, &d__2) * pow_dd(&x3, &d__3)
                * pow_dd(&x4, &slap_1.a5);
        d__1 = slap_1.a0 * slap_1.xgeom;
        d__2 = slap_1.a4 + slap_1.a1 * (slap_1.omega - 2.f);
        d__3 = slap_1.a5 + 1.f;
        *h_fun__ = pow_dd(&x1, &d__1) * pow_dd(&x3, &d__2) * pow_dd(&x4, &d__3);
    }
    return 0;
} /* sedov_funcs__ */

/* Subroutine */int midpnt_(D_fp func, sed_real *a, sed_real *b, sed_real *s,
        int *n) {
    /* System generated locals */
    int i__1;
    sed_real d__1;

    /* Builtin functions */
    int pow_ii(int *, int *);

    /* Local variables */
    static int j;
    static sed_real x;
    static int it;
    static sed_real del, tnm, sum, ddel;

    /* ..this routine computes the n'th stage of refinement of an extended midpoint */
    /* ..rule. func is input as the name of the function to be integrated between */
    /* ..limits a and b. when called with n=1, the routine returns as s the crudest */
    /* ..estimate of the integralof func from a to b. subsequent calls with n=2,3... */
    /* ..improve the accuracy of s by adding 2/3*3**(n-1) addtional interior points. */
    /* ..declare */
    if (*n == 1) {
        d__1 = (*a + *b) * .5f;
        *s = (*b - *a) * (*func)(&d__1);
    } else {
        i__1 = *n - 2;
        it = pow_ii(&c__3, &i__1);
        tnm = static_cast<sed_real>(it);
        del = (*b - *a) / (tnm * 3.f);
        ddel = del + del;
        x = *a + del * .5f;
        sum = 0.f;
        i__1 = it;
        for (j = 1; j <= i__1; ++j) {
            sum += (*func)(&x);
            x += ddel;
            sum += (*func)(&x);
            x += del;
        }
        *s = (*s + (*b - *a) * sum / tnm) / 3.f;
    }
    return 0;
} /* midpnt_ */

/* Subroutine */int midpowl_(D_fp funk, sed_real *aa, sed_real *bb, sed_real *s,
        int *n) {
    /* System generated locals */
    int i__1;
    sed_real d__1, d__2, d__3, d__4;

    /* Builtin functions */
    sed_real pow_dd(sed_real *, sed_real *);
    int pow_ii(int *, int *);

    /* Local variables */
    static sed_real a, b;
    static int j;
    static sed_real x;
    static int it;
    static sed_real del, tnm, sum, ddel;

    /* ..this routine is an exact replacement for midpnt, except that it allows for */
    /* ..an integrable power-law singularity of the form (x - a)**(-gam_int) */
    /* ..at the lower limit aa for 0 < gam_int < 1. */
    /* ..declare */
    /* ..common block communication */
    /* ..a little conversion, recipe equation 4.4.3 */
    d__1 = *bb - *aa;
    d__2 = 1.f - cmidp_1.gam_int__;
    b = pow_dd(&d__1, &d__2);
    a = 0.f;
    /* ..now exactly as midpnt */
    if (*n == 1) {
        d__1 = (a + b) * .5f;
        d__2 = cmidp_1.gam_int__ / (1.f - cmidp_1.gam_int__);
        d__4 = 1.f / (1.f - cmidp_1.gam_int__);
        d__3 = pow_dd(&d__1, &d__4) + *aa;
        *s = (b - a)
                * (1.f / (1.f - cmidp_1.gam_int__) * pow_dd(&d__1, &d__2)
                        * (*funk)(&d__3));
    } else {
        i__1 = *n - 2;
        it = pow_ii(&c__3, &i__1);
        tnm = static_cast<sed_real>(it);
        del = (b - a) / (tnm * 3.f);
        ddel = del + del;
        x = a + del * .5f;
        sum = 0.f;
        i__1 = it;
        for (j = 1; j <= i__1; ++j) {
            d__1 = cmidp_1.gam_int__ / (1.f - cmidp_1.gam_int__);
            d__3 = 1.f / (1.f - cmidp_1.gam_int__);
            d__2 = pow_dd(&x, &d__3) + *aa;
            sum += 1.f / (1.f - cmidp_1.gam_int__) * pow_dd(&x, &d__1)
                    * (*funk)(&d__2);
            x += ddel;
            d__1 = cmidp_1.gam_int__ / (1.f - cmidp_1.gam_int__);
            d__3 = 1.f / (1.f - cmidp_1.gam_int__);
            d__2 = pow_dd(&x, &d__3) + *aa;
            sum += 1.f / (1.f - cmidp_1.gam_int__) * pow_dd(&x, &d__1)
                    * (*funk)(&d__2);
            x += del;
        }
        *s = (*s + (b - a) * sum / tnm) / 3.f;
    }
    return 0;
} /* midpowl_ */

/* Subroutine */int midpowl2_(D_fp funk, sed_real *aa, sed_real *bb, sed_real *s,
        int *n) {
    /* System generated locals */
    int i__1;
    sed_real d__1, d__2, d__3, d__4;

    /* Builtin functions */

    /* Local variables */
    static sed_real a, b;
    static int j;
    static sed_real x;
    static int it;
    static sed_real del, tnm, sum, ddel;

    /* ..this routine is an exact replacement for midpnt, except that it allows for */
    /* ..an integrable power-law singularity of the form (a - x)**(-gam_int) */
    /* ..at the lower limit aa for 0 < gam_int < 1. */
    /* ..declare */
    /* ..common block communication */
    /* ..a little conversion, modulo recipe equation 4.4.3 */
    d__1 = *aa - *bb;
    d__2 = 1.f - cmidp_1.gam_int__;
    b = pow_dd(&d__1, &d__2);
    a = 0.f;
    /* ..now exactly as midpnt */
    if (*n == 1) {
        d__1 = (a + b) * .5f;
        d__2 = cmidp_1.gam_int__ / (1.f - cmidp_1.gam_int__);
        d__4 = 1.f / (1.f - cmidp_1.gam_int__);
        d__3 = *aa - pow_dd(&d__1, &d__4);
        *s = (b - a)
                * (1.f / (cmidp_1.gam_int__ - 1.f) * pow_dd(&d__1, &d__2)
                        * (*funk)(&d__3));
    } else {
        i__1 = *n - 2;
        it = pow_ii(&c__3, &i__1);
        tnm = static_cast<sed_real>(it);
        del = (b - a) / (tnm * 3.f);
        ddel = del + del;
        x = a + del * .5f;
        sum = 0.f;
        i__1 = it;
        for (j = 1; j <= i__1; ++j) {
            d__1 = cmidp_1.gam_int__ / (1.f - cmidp_1.gam_int__);
            d__3 = 1.f / (1.f - cmidp_1.gam_int__);
            d__2 = *aa - pow_dd(&x, &d__3);
            sum += 1.f / (cmidp_1.gam_int__ - 1.f) * pow_dd(&x, &d__1)
                    * (*funk)(&d__2);
            x += ddel;
            d__1 = cmidp_1.gam_int__ / (1.f - cmidp_1.gam_int__);
            d__3 = 1.f / (1.f - cmidp_1.gam_int__);
            d__2 = *aa - pow_dd(&x, &d__3);
            sum += 1.f / (cmidp_1.gam_int__ - 1.f) * pow_dd(&x, &d__1)
                    * (*funk)(&d__2);
            x += del;
        }
        *s = (*s + (b - a) * sum / tnm) / 3.f;
    }
    return 0;
} /* midpowl2_ */

/* Subroutine */int qromo_(D_fp func, sed_real *a, sed_real *b, sed_real *eps,
        sed_real *ss, S_fp choose) {
    /* Builtin functions */

    /* Local variables */
    static sed_real h__[15];
    static int j;
    static sed_real s[15], dss;

    /* Fortran I/O blocks */

    /* ..this routine returns as s the integral of the function func from a to b */
    /* ..with fractional accuracy eps. *//* ..jmax limits the number of steps; nsteps = 3**(jmax-1) */
    /* ..integration is done via romberg algorithm. */
    /* ..it is assumed the call to choose triples the number of steps on each call */
    /* ..and that its error series contains only even powers of the number of steps. */
    /* ..the external choose may be any of the above drivers, i.e midpnt,midinf... */
    /* ..declare */
    h__[0] = 1.f;
    for (j = 1; j <= 14; ++j) {
        (*choose)(static_cast<D_fp>(func), a, b, &s[j - 1], &j);
        if (j >= 5) {
            polint_(&h__[j - 5], &s[j - 5], &c__5, &c_b79, ss, &dss);
            if (fabs(static_cast<double>(dss)) <= *eps * fabs(static_cast<double>(*ss))) {
                return 0;
            }
        }
        s[j] = s[j - 1];
        h__[j] = h__[j - 1] / 9.f;
    }
    return 0;
} /* qromo_ */

/* Subroutine */int polint_(sed_real *xa, sed_real *ya, int *n, sed_real *x,
        sed_real *y, sed_real *dy) {
    /* System generated locals */
    int i__1, i__2;
    sed_real d__1;

    /* Local variables */
    static sed_real c__[20], d__[20];
    static int i__, m;
    static sed_real w, ho, hp;
    static int ns;
    static sed_real dif, den, dift;

    /* ..given arrays xa and ya of length n and a value x, this routine returns a */
    /* ..value y and an error estimate dy. if p(x) is the polynomial of degree n-1 */
    /* ..such that ya = p(xa) ya then the returned value is y = p(x) */
    /* ..declare */
    /* ..find the index ns of the closest table entry; initialize the c and d tables */
    /* Parameter adjustments */
    --ya;
    --xa;

    /* Function Body */
    ns = 1;
    dif = (d__1 = *x - xa[1], fabs(static_cast<double>(d__1)));
    i__1 = *n;
    for (i__ = 1; i__ <= i__1; ++i__) {
        dift = (d__1 = *x - xa[i__], fabs(static_cast<double>(d__1)));
        if (dift < dif) {
            ns = i__;
            dif = dift;
        }
        c__[i__ - 1] = ya[i__];
        d__[i__ - 1] = ya[i__];
    }
    /* ..first guess for y */
    *y = ya[ns];
    /* ..for each column of the table, loop over the c's and d's and update them */
    --ns;
    i__1 = *n - 1;
    for (m = 1; m <= i__1; ++m) {
        i__2 = *n - m;
        for (i__ = 1; i__ <= i__2; ++i__) {
            ho = xa[i__] - *x;
            hp = xa[i__ + m] - *x;
            w = c__[i__] - d__[i__ - 1];
            den = ho - hp;
            if (den == 0.f) {
    //			s_stop(" 2 xa entries are the same in polint", (ftnlen) 36);
            }
            den = w / den;
            d__[i__ - 1] = hp * den;
            c__[i__ - 1] = ho * den;
        }
        /* ..after each column is completed, decide which correction c or d, to add */
        /* ..to the accumulating value of y, that is, which path to take in the table */
        /* ..by forking up or down. ns is updated as we go to keep track of where we */
        /* ..are. the last dy added is the error indicator. */
        if (ns << 1 < *n - m) {
            *dy = c__[ns];
        } else {
            *dy = d__[ns - 1];
            --ns;
        }
        *y += *dy;
    }
    return 0;
} /* polint_ */

sed_real zeroin_(sed_real *ax, sed_real *bx, D_fp f, sed_real *tol) {
    /* System generated locals */
    sed_real ret_val, d__1;
    ret_val = 0;

    /* Local variables */
    static sed_real a, b, c__, d__, e, p, q, r__, s, fa, fb, fc, xm, eps, tol1;

    /* ----------------------------------------------------------------------- */

    /* This subroutine solves for a zero of the function  f(x)  in the */
    /* interval ax,bx. */

    /*  input.. */

    /*  ax     left endpoint of initial interval */
    /*  bx     right endpoint of initial interval */
    /*  f      function subprogram which evaluates f(x) for any x in */
    /*         the interval  ax,bx */
    /*  tol    desired length of the interval of uncertainty of the */
    /*         final result ( .ge. 0.0) */

    /*  output.. */

    /*  zeroin abcissa approximating a zero of  f  in the interval ax,bx */

    /*      it is assumed  that   f(ax)   and   f(bx)   have  opposite  signs */
    /*  without  a  check.  zeroin  returns a zero  x  in the given interval */
    /*  ax,bx  to within a tolerance  4*macheps*fabs(x) + tol, where macheps */
    /*  is the relative machine precision. */
    /*      this function subprogram is a slightly  modified  translation  of */
    /*  the algol 60 procedure  zero  given in  richard brent, algorithms for */
    /*  minimization without derivatives, prentice - hall, inc. (1973). */

    /* ----------------------------------------------------------------------- */
    /* .... call list variables */

    /* ---------------------------------------------------------------------- */

    /*  compute eps, the relative machine precision */

    eps = 1.f;
    L10: eps /= 2.f;
    tol1 = eps + 1.f;
    if (tol1 > 1.f) {
        goto L10;
    }

    /* initialization */

    a = *ax;
    b = *bx;
    fa = (*f)(&a);
    fb = (*f)(&b);

    /* begin step */

    L20: c__ = a;
    fc = fa;
    d__ = b - a;
    e = d__;
    L30: if (fabs(static_cast<double>(fc)) >= fabs(static_cast<double>(fb))) {
        goto L40;
    }
    a = b;
    b = c__;
    c__ = a;
    fa = fb;
    fb = fc;
    fc = fa;

    /* convergence test */

    L40: tol1 = eps * 2.f * fabs(static_cast<double>(b)) + *tol * .5f;
    xm = (c__ - b) * .5f;
    if (fabs(static_cast<double>(xm)) <= tol1) {
        goto L90;
    }
    if (fb == 0.f) {
        goto L90;
    }

    /* is bisection necessary? */

    if (fabs(static_cast<double>(e)) < tol1) {
        goto L70;
    }
    if (fabs(static_cast<double>(fa)) <= fabs(static_cast<double>(fb))) {
        goto L70;
    }

    /* is quadratic interpolation possible? */

    if (a != c__) {
        goto L50;
    }

    /* linear interpolation */

    s = fb / fa;
    p = xm * 2.f * s;
    q = 1.f - s;
    goto L60;

    /* inverse quadratic interpolation */

    L50: q = fa / fc;
    r__ = fb / fc;
    s = fb / fa;
    p = s * (xm * 2.f * q * (q - r__) - (b - a) * (r__ - 1.f));
    q = (q - 1.f) * (r__ - 1.f) * (s - 1.f);

    /* adjust signs */

    L60: if (p > 0.f) {
        q = -q;
    }
    p = fabs(static_cast<double>(p));

    /* is interpolation acceptable? */

    if (p * 2.f >= xm * 3.f * q - (d__1 = tol1 * q, fabs(static_cast<double>(d__1)))) {
        goto L70;
    }
    if (p >= (d__1 = e * .5f * q, fabs(static_cast<double>(d__1)))) {
        goto L70;
    }
    e = d__;
    d__ = p / q;
    goto L80;

    /* bisection */

    L70: d__ = xm;
    e = d__;

    /* complete step */

    L80: a = b;
    fa = fb;
    if (fabs(static_cast<double>(d__)) > tol1) {
        b += d__;
    }
    if (fabs(static_cast<double>(d__)) <= tol1) {
        b += d_sign(&tol1, &xm);
    }
    fb = (*f)(&b);
    if (fb * (fc / fabs(static_cast<double>(fc))) > 0.f) {
        goto L20;
    }
    goto L30;

    /* done */

    L90: ret_val = b;
    return ret_val;
} /* zeroin_ */

#include <functional>
#include <mutex>

#ifndef NO_HPX
#include <hpx/synchronization/spinlock.hpp>
using mutex_type = hpx::lcos::local::spinlock;
#else
#include <unordered_map>
#include <memory>
#include <cassert>
using mutex_type = std::mutex;
#endif

namespace sedov {

void solution(double time, double r, double rmax, double& d, double& v, double& p, int ndim) {
	int nstep = 10000;
	constexpr int bw = 2;
	using function_type = std::function<void(double,double&,double&,double&)>;
	using map_type = std::unordered_map<double,std::shared_ptr<function_type>>;

	static map_type map;
	static mutex_type mutex;

	sed_real rho0 = 1.0;
	sed_real vel0 = 0.0;
	sed_real ener0 = 0.0;
	sed_real pres0 = 0.0;
	sed_real cs0 = 0.0;
	sed_real gamma = 7.0/5.0;
	sed_real omega = 0.0;
	sed_real eblast = 1.0;
	sed_real xgeom = sed_real(ndim);

	std::vector<sed_real> xpos(nstep+2*bw);
	std::vector<sed_real> den(nstep+2*bw);
	std::vector<sed_real> ener(nstep+2*bw);
	std::vector<sed_real> pres(nstep+2*bw);
	std::vector<sed_real> vel(nstep+2*bw);
	std::vector<sed_real> cs(nstep+2*bw);

	std::vector<double> den1(nstep+2*bw);
	std::vector<double> pres1(nstep+2*bw);
	std::vector<double> vel1(nstep+2*bw);

	std::shared_ptr<function_type> ptr;

	for( int i = 0; i < nstep + 2*bw; i++) {
		xpos[i] = (i - bw + 0.5)*rmax/(nstep);
	}
	nstep += bw;

	std::unique_lock<mutex_type> lock(mutex);
	auto iter = map.find(time);
	if (iter == map.end()) {
		sed_real sed_time = time;
		printf( "Computing sedov solution\n");
		sed_1d__(&sed_time, &nstep, xpos.data() + bw, &eblast, &omega, &xgeom, &rho0,
				&vel0, &ener0, &pres0, &cs0, &gamma, den.data() + bw, ener.data() + bw,
				pres.data() + bw, vel.data() + bw, cs.data() + bw);

		xpos[0] = -xpos[3];
		den[0] = den[3];
		ener[0] = ener[3];
		pres[0] = pres[3];
		vel[0] = -vel[3];
		cs[0] = cs[3];

		xpos[1] = -xpos[2];
		den[1] = den[2];
		ener[1] = ener[2];
		pres[1] = pres[2];
		vel[1] = -vel[2];
		cs[1] = cs[2];

#if defined(OCTOTIGER_HAVE_BOOST_MULTIPRECISION)
		std::transform(den.begin(), den.end(), den1.begin(),
			[](sed_real v) { return v.convert_to<double>(); });
		std::transform(vel.begin(), vel.end(), vel1.begin(),
			[](sed_real v) { return v.convert_to<double>(); });
		std::transform(pres.begin(), pres.end(), pres1.begin(),
			[](sed_real v) { return v.convert_to<double>(); });
#else
		std::copy(den.begin(), den.end(), den1.begin());
		std::copy(vel.begin(), vel.end(), vel1.begin());
		std::copy(pres.begin(), pres.end(), pres1.begin());
#endif

		function_type func = [nstep,rmax,den1,pres1,vel1,bw](double r, double& d, double& v, double & p) {
			double dr = rmax / (nstep);
			std::array<int,4> i;
			i[1] = (r + (bw - 0.5)*dr) / dr;
			i[0] = i[1] - 1;
			i[2] = i[1] + 1;
			i[3] = i[1] + 2;
			double r0 = (r - (i[1]-bw + 0.5)*dr)/dr;
	//		printf( "%i %e\n", i[0], r, dr );
			assert( i[0] >= 0 );
			assert( i[3] < int(vel1.size()));
			const auto interp = [r0,i](const std::vector<double>& data) {
				double sum = 0.0;
				sum += (-0.5 * data[i[0]] + 1.5 * data[i[1]] - 1.5 * data[i[2]] + 0.5 * data[i[3]]) * r0 * r0 * r0;
				sum += (+1.0 * data[i[0]] - 2.5 * data[i[1]] + 2.0 * data[i[2]] - 0.5 * data[i[3]]) * r0 * r0;
				sum += (-0.5 * data[i[0]]                   +  0.5 * data[i[2]]) * r0;
				sum += data[i[1]];
				return sum;
			};

			d = interp(den1);
			v = interp(vel1);
			p = interp(pres1);

		};

		ptr = std::make_shared<function_type>(std::move(func));
		map[time] = ptr;
		lock.unlock();
	} else {
		lock.unlock();
		ptr = iter->second;
	}

	const auto& func = *(ptr);

	func(r, d, v, p);
}

}


#ifndef InitCondLV_CC_
#define InitCondLV_CC_
/**
 * @file initcondlv.cc
 * @brief NPDE homework InitCondLV code
 * @author lfilippo, tille, jgacon, dcasati
 * @copyright Developed at ETH Zurich
 */

#include <Eigen/Core>
#include <iostream>
#include <utility>

#include "../../../lecturecodes/Ode45/ode45.h"

namespace InitCondLV {

/* Compute the maps Phi(t,y0) and W(t,y0) at final time T.
 * Use initial data given by u0 and v0. */
/* SAM_LISTING_BEGIN_1 */
std::pair<Eigen::Vector2d, Eigen::Matrix2d> PhiAndW(double u0, double v0,
                                                    double T) {
  // Save the values of Phi and W at time T in PaW.first and PaW.second resp.
  std::pair<Eigen::Vector2d, Eigen::Matrix2d> PaW;

  //====================
  // Your code goes here
  //====================
  return PaW;
}
/* SAM_LISTING_END_1 */

}  // namespace InitCondLV

#endif  // #define InitCondLV_CC_


///
/// @author  Thomas Lehmann
/// @file    factorial.cxx
/// @brief   factorial function
///
/// Copyright (c) 2015 Thomas Lehmann
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
/// documentation files (the "Software"), to deal in the Software without restriction, including without limitation
/// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
/// and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in all copies
/// or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
/// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
/// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
/// DAMAGES OR OTHER LIABILITY,
/// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <generator/select.h>
#include <math/big_integer.h>
#include <math/big_integer_configurator.h>
#include <performance/measurement.h>

#include <boost/program_options.hpp>
#include <vector>
#include <cstdint>

/// @struct options
/// @brief parsed command line options
struct Options {
    uint64_t max_n; ///! factorial: n!
    bool all;       ///! when true showing each factorial
};

/// @param argc number of parameters
/// @param argv array of parameters
/// @param options where to store the parsed command line options
/// @return true when all is fine and application can use the option(s).
static bool parse(int argc, char** argv, Options& options) {
    namespace po = boost::program_options;
    po::options_description description("Allowed options for tool 'factorial'");
    description.add_options()
        ("help", "print this help")
        ("n", po::value<uint64_t>(&options.max_n)->default_value(100),
         "Calculating factorial: n! (default: 100)")
        ("all", po::value<bool>(&options.all)->default_value(true),
         "showing each factorials until 'n!' (default: true)")
        ;

    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, description), vm);
    po::notify(vm);

    if (vm.count("help")) {
        std::cout << description << std::endl;
        return false;
    }

    return true;
}

/// Simple example demonstrating how to generate factorials.
///
/// @param argc number of parameters
/// @param argv array of parameters
/// @return 0 when succeeded, 1 when failed or when used the help
int main(int argc, char** argv) {
    std::cout << "factorial tool (version " << VERSION << ")" << std::endl;

    Options options;
    // parsing command line options
    if (!parse(argc, argv, options)) {
        return 1;
    }

    // registering implementations
    math::big_integer_configurator bic;
    bic.configure();

    math::big_integer bi = 1;
    std::cout << options.max_n << "!" << std::endl << std::endl;

    std::vector<math::big_integer> results;

    const auto duration = performance::measure<std::milli>([&options, &bi, &results]() {
        for (auto n = static_cast<uint64_t>(2); n <= options.max_n; ++n) {
            bi *= math::big_integer(n);
            if (options.all) {
                results.push_back(bi);
            }
        }
    });

    if (!options.all) {
        results.push_back(bi);
    }

    for (const auto& result: results) {
        std::cout << result.to_string() << std::endl;
    }

    std::cout << std::endl;
    std::cout << " ... " << bi.size() << " digits." << std::endl;
    std::cout << " ... Calculation only took " << duration << "ms." << std::endl;
    return 0;
}


/* vim: set tabstop=4 expandtab shiftwidth=4 softtabstop=4: */

/**
 * \file libs/numeric/ublasx/test/eps.cpp.
 *
 * \brief Test suite for the \c eps operation.
 *
 * \author Marco Guazzone (marco.guazzone@gmail.com)
 *
 * <hr/>
 *
 * Copyright (c) 2010, Marco Guazzone
 *
 * 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)
 */

#include <boost/numeric/ublasx/detail/debug.hpp>
#include <boost/numeric/ublasx/operation/eps.hpp>
#include <cmath>
#include "libs/numeric/ublasx/test/utils.hpp"
#include <limits>

namespace ublasx = boost::numeric::ublasx;

const float float_tol = ::std::numeric_limits<float>::epsilon()*2.0;
const double double_tol = ::std::numeric_limits<double>::epsilon()*2.0;

BOOST_UBLASX_TEST_DEF( float_no_arg )
{
    BOOST_UBLASX_DEBUG_TRACE("Test Case: Float type - No arg");

    typedef float value_type;

    value_type eps = ublasx::eps<value_type>();
    value_type expect_eps = ::std::numeric_limits<value_type>::epsilon();

    BOOST_UBLASX_DEBUG_TRACE("eps = " << eps);
    BOOST_UBLASX_TEST_CHECK_CLOSE(eps, expect_eps, float_tol);
}

BOOST_UBLASX_TEST_DEF( float_scalar_arg )
{
    BOOST_UBLASX_DEBUG_TRACE("Test Case: Float type - Scalar arg");

    typedef float value_type;

    value_type val;
    value_type eps;
    value_type expect_eps;

    val = 0;
    eps = ublasx::eps<value_type>(val);
    expect_eps = ::std::numeric_limits<value_type>::denorm_min();
    BOOST_UBLASX_DEBUG_TRACE("val = " << val);
    BOOST_UBLASX_DEBUG_TRACE("eps = " << eps);
    BOOST_UBLASX_TEST_CHECK_CLOSE(eps, expect_eps, float_tol);

    val = ::std::numeric_limits<value_type>::min();
    eps = ublasx::eps<value_type>(val);
    expect_eps = ::std::numeric_limits<value_type>::denorm_min();
    BOOST_UBLASX_DEBUG_TRACE("val = " << val);
    BOOST_UBLASX_DEBUG_TRACE("eps = " << eps);
    BOOST_UBLASX_TEST_CHECK_CLOSE(eps, expect_eps, float_tol);

    val = ::std::numeric_limits<value_type>::min()/static_cast<value_type>(2);
    eps = ublasx::eps<value_type>(val);
    expect_eps = ::std::numeric_limits<value_type>::denorm_min();
    BOOST_UBLASX_DEBUG_TRACE("val = " << val);
    BOOST_UBLASX_DEBUG_TRACE("eps = " << eps);
    BOOST_UBLASX_TEST_CHECK_CLOSE(eps, expect_eps, float_tol);

    val = ::std::numeric_limits<value_type>::infinity();
    eps = ublasx::eps<value_type>(val);
    BOOST_UBLASX_DEBUG_TRACE("val = " << val);
    BOOST_UBLASX_DEBUG_TRACE("eps = " << eps);
    BOOST_UBLASX_TEST_CHECK(std::isnan(eps));

    val = ::std::numeric_limits<value_type>::quiet_NaN();
    eps = ublasx::eps<value_type>(val);
    BOOST_UBLASX_DEBUG_TRACE("val = " << val);
    BOOST_UBLASX_DEBUG_TRACE("eps = " << eps);
    BOOST_UBLASX_TEST_CHECK(std::isnan(eps));
}

BOOST_UBLASX_TEST_DEF( double_no_arg )
{
    BOOST_UBLASX_DEBUG_TRACE("Test Case: Double type - No arg");

    typedef double value_type;

    value_type eps = ublasx::eps<value_type>();
    value_type expect_eps = ::std::numeric_limits<value_type>::epsilon();

    BOOST_UBLASX_DEBUG_TRACE("eps = " << eps);
    BOOST_UBLASX_TEST_CHECK_CLOSE(eps, expect_eps, double_tol);
}

BOOST_UBLASX_TEST_DEF( double_scalar_arg )
{
    BOOST_UBLASX_DEBUG_TRACE("Test Case: Double type - Scalar arg");

    typedef double value_type;

    value_type val;
    value_type eps;
    value_type expect_eps;

    val = 0;
    eps = ublasx::eps<value_type>(val);
    expect_eps = ::std::numeric_limits<value_type>::denorm_min();
    BOOST_UBLASX_DEBUG_TRACE("val = " << val);
    BOOST_UBLASX_DEBUG_TRACE("eps = " << eps);
    BOOST_UBLASX_TEST_CHECK_CLOSE(eps, expect_eps, double_tol);

    val = ::std::numeric_limits<value_type>::min();
    eps = ublasx::eps<value_type>(val);
    expect_eps = ::std::numeric_limits<value_type>::denorm_min();
    BOOST_UBLASX_DEBUG_TRACE("val = " << val);
    BOOST_UBLASX_DEBUG_TRACE("eps = " << eps);
    BOOST_UBLASX_TEST_CHECK_CLOSE(eps, expect_eps, double_tol);

    val = ::std::numeric_limits<value_type>::min()/static_cast<value_type>(2);
    eps = ublasx::eps<value_type>(val);
    expect_eps = ::std::numeric_limits<value_type>::denorm_min();
    BOOST_UBLASX_DEBUG_TRACE("val = " << val);
    BOOST_UBLASX_DEBUG_TRACE("eps = " << eps);
    BOOST_UBLASX_TEST_CHECK_CLOSE(eps, expect_eps, double_tol);

    val = ::std::numeric_limits<value_type>::infinity();
    eps = ublasx::eps<value_type>(val);
    BOOST_UBLASX_DEBUG_TRACE("val = " << val);
    BOOST_UBLASX_DEBUG_TRACE("eps = " << eps);
    BOOST_UBLASX_TEST_CHECK(std::isnan(eps));

    val = ::std::numeric_limits<value_type>::quiet_NaN();
    eps = ublasx::eps<value_type>(val);
    BOOST_UBLASX_DEBUG_TRACE("val = " << val);
    BOOST_UBLASX_DEBUG_TRACE("eps = " << eps);
    BOOST_UBLASX_TEST_CHECK(std::isnan(eps));
}

int main()
{
    BOOST_UBLASX_DEBUG_TRACE("Test Suite: 'eps' operations");

    BOOST_UBLASX_TEST_BEGIN();

    BOOST_UBLASX_TEST_DO( float_no_arg )
    BOOST_UBLASX_TEST_DO( float_scalar_arg )
    BOOST_UBLASX_TEST_DO( double_no_arg )
    BOOST_UBLASX_TEST_DO( double_scalar_arg )

    BOOST_UBLASX_TEST_END();
}


#include <iostream>
#include <fstream>
#include <string>
#include <Eigen/Dense>
#include <conio.h>
#include<vector>
using namespace std;
using namespace Eigen;

MatrixXd Data(string fileToOpen)
{

    vector<double> matrixEntries;
    ifstream matrixDataFile(fileToOpen);
    string matrixRowString;
    string matrixEntry;

    int matrixRowNumber = 0;

    while (getline(matrixDataFile, matrixRowString))
    {
        stringstream matrixRowStringStream(matrixRowString);

        while (getline(matrixRowStringStream, matrixEntry, ','))
        {
            matrixEntries.push_back(stod(matrixEntry));
        }
        matrixRowNumber++;
    }

    return Map<Matrix<double, Dynamic, Dynamic, RowMajor>>(matrixEntries.data(), matrixRowNumber, matrixEntries.size() / matrixRowNumber);

}

void InvertirGaussJordan()
{
    MatrixXd m = Data("Texto.txt"), I(m.rows(),m.rows());
    int c = m.rows();
    //Se crea la matriz identidad
    for (int x = 0; x < c; x++)
    {
        for (int y = 0; y < c; y++)
        {
            I(x, y) = 0;
            if (x == y)
            {
                I(x, y) = 1;

            }
        }
    }
    cout << "Matriz inicial:\n " << m << endl;
    cout << "Matriz identidad:\n " << I << endl;
    cout << m.cols()<<endl;
    /*Para lograr que la diagonal de la matriz sea 1, se divide cada elemento de la diagonal por el mismo y por su
    fila correspondiente, igual con la matriz identidad.*/
    for (int x = 0; x < c; x++)
    {
        float v = m(x, x);

        for (int y = 0; y < c; y++)
        {

            m(x, y) = m(x, y)*(1/v);
            I(x, y) = I(x, y)*(1/v);
        }

        for (int j = 0; j < c; j++)
        {
            //verifico no estar en diagonal y empiezo a hacer GJ:
            if (x != j)
            {
                float  s = m(j, x);
                for (int k = 0; k < c; k++)
                {
                    m(j, k) = m(j, k) - (s * m(x, k));
                    I(j, k) = I(j, k) - (s * I(x, k));
                }

            }

        }
    }
    cout << "Inversa:\n " << I << endl;
    fstream archivo;
    archivo.open("Texto.txt", ios::app);
    if (archivo.is_open())
    {
        archivo <<"La inversa es: \n"<< I;
        archivo.close();
    }
}

int determinantecofactor(Matrix2d x)
{
    int resultado;
    resultado = x(0, 0) * x(1, 1) - x(0, 1) * (1, 0);
    return resultado;
}

void determinante(MatrixXd m)
{
    //intento de una funcion recursiva que calcula el determinante por cofactores.
     if (m.rows() == 2)
     {
         return determinantecofactor(m);
     }
    vector<int> f;

        for (int i = 0; i < m.rows(); i++)
        {
            for (int j = 0; j < m.rows(); j++)
            {
                for (int k = 0; k < m.rows(); k++)
                {
                    if ((j == i) && (k == 0))
                    {
                        continue;

                    }
                    MatrixXd m_l(m.rows() - 1, m.rows() - 1);
                    m_l(l,i) = m(j, k);
                    f.push_back(m(j, k));
                }

            }
        }

        cout << int(f.size()) << endl;
}
int main()
{

    InvertirGaussJordan();
    determinante(Data("Texto.txt"));
    return 0;

}


#include <Eigen/Core>
#include <Eigen/Sparse>
#include <iostream>

void gradient_descent_test(const std::function<double(const double)> f, const std::function<double(const double)> gf, const double x0, double & fx, double & x, std::vector<double> & intervals, std::vector<double> & values, std::vector<double> & minima){
//    for (int i = 0; i<intervals.size(); i++) {
//        std::cout << " " << intervals[i];
//    }
    //std::cout << std::endl;
    int max_iter = 100;
    //double alpha = 0.02;
    double alpha = 0.02; // CHANGE THIS BACK
    double tau,g;
    double min = 0.0;
    double max = 1.0;
    double tol = 1e-6;
    double xmin = x0;
    double xmax = x0;
    x = x0;

    double prev_x = 10000000.0;
    int iter = 0;
    bool stop = false;
    double x_candidate,fx_candidate;
    g = 100.0;
    //std::cout << "run" << std::endl;
    int in_existing_interval = -1;
    assert(iter<max_iter && !stop && abs(x-prev_x)>tol);
    while (iter<max_iter && !stop && abs(x-prev_x)>tol) {
        xmin = std::min(xmin,x);
        xmax = std::max(xmax,x);

        for (int mm = 0; mm < (intervals.size()/2); mm++) {
            if ((x >= (intervals[2*mm]-1e-6)) && (x <= (intervals[2*mm+1]+1e-6)) ){
                fx = values[mm];
                x = minima[mm];
                in_existing_interval = mm;
                break;
             //   in_existing_interval = true;
            }
        }
        if (in_existing_interval>-1) {
            break;
        }
        if (iter==0) {
            fx = f(x);
        }
//        if (fx<-0.1) {
//            break;
//        }

        g = gf(x);
        //std::cout << g << std::endl;
        tau = alpha;
        prev_x = x;
        for (int div = 1; div<10; div++) {
            iter = iter + 1;
            assert(iter<max_iter);
            x_candidate = x - tau* ( (double) (g > 0) - (g < 0));
            x_candidate = std::max(std::min(x_candidate,1.0),0.0);
            fx_candidate = f(x_candidate);
            if ((fx_candidate-fx)<(0.5*(x_candidate - x)*g)) {
                x = x_candidate;
                fx = fx_candidate;
                //std::cout << div << std::endl;
                break;
            }
            tau = 0.5*tau;
            if (div==9) {
                //std::cout << div << std::endl;
                stop = true;
            }
        }
    }

    if (in_existing_interval==-1) {
        // we have discovered a new interval
        intervals.push_back(xmin);
        intervals.push_back(xmax);
        values.push_back(fx);
        minima.push_back(x);
    }else{
        // grow interval
        intervals[2*in_existing_interval] = std::min(intervals[2*in_existing_interval],xmin);
        intervals[2*in_existing_interval+1] = std::max(intervals[2*in_existing_interval+1],xmax);
    }

  //  std::cout << iter << std::endl;

//    for (int i = 0; i<intervals.size(); i++) {
//        std::cout << " " << intervals[i];
//    }
//    std::cout << std::endl;

}


/**
 * @file loss_functions_test.cpp
 * @author Dakshit Agrawal
 * @author Sourabh Varshney
 * @author Atharva Khandait
 * @author Saksham Rastogi
 *
 * Tests for loss functions in mlpack::methods::ann:loss_functions.
 *
 * mlpack is free software; you may redistribute it and/or modify it under the
 * terms of the 3-clause BSD license.  You should have received a copy of the
 * 3-clause BSD license along with mlpack.  If not, see
 * http://www.opensource.org/licenses/BSD-3-Clause for more information.
 */
#include <mlpack/core.hpp>

#include <mlpack/methods/ann/layer/layer.hpp>
#include <mlpack/methods/ann/loss_functions/kl_divergence.hpp>
#include <mlpack/methods/ann/loss_functions/earth_mover_distance.hpp>
#include <mlpack/methods/ann/loss_functions/mean_squared_error.hpp>
#include <mlpack/methods/ann/loss_functions/sigmoid_cross_entropy_error.hpp>
#include <mlpack/methods/ann/loss_functions/cross_entropy_error.hpp>
#include <mlpack/methods/ann/loss_functions/reconstruction_loss.hpp>
#include <mlpack/methods/ann/loss_functions/mean_squared_logarithmic_error.hpp>
#include <mlpack/methods/ann/loss_functions/mean_bias_error.hpp>
#include <mlpack/methods/ann/loss_functions/dice_loss.hpp>
#include <mlpack/methods/ann/loss_functions/log_cosh_loss.hpp>
#include <mlpack/methods/ann/init_rules/nguyen_widrow_init.hpp>
#include <mlpack/methods/ann/ffn.hpp>

#include <boost/test/unit_test.hpp>
#include "test_tools.hpp"
#include "ann_test_tools.hpp"

using namespace mlpack;
using namespace mlpack::ann;

BOOST_AUTO_TEST_SUITE(LossFunctionsTest);

/**
 * Simple KL Divergence test.  The loss should be zero if input = target.
 */
BOOST_AUTO_TEST_CASE(SimpleKLDivergenceTest)
{
  arma::mat input, target, output;
  double loss;
  KLDivergence<> module(true);

  // Test the Forward function.  Loss should be 0 if input = target.
  input = arma::ones(10, 1);
  target = arma::ones(10, 1);
  loss = module.Forward(std::move(input), std::move(target));
  BOOST_REQUIRE_SMALL(loss, 0.00001);
}

/*
 * Simple test for the mean squared logarithmic error function.
 */
BOOST_AUTO_TEST_CASE(SimpleMeanSquaredLogarithmicErrorTest)
{
  arma::mat input, output, target;
  MeanSquaredLogarithmicError<> module;

  // Test the Forward function on a user generator input and compare it against
  // the manually calculated result.
  input = arma::zeros(1, 8);
  target = arma::zeros(1, 8);
  double error = module.Forward(std::move(input), std::move(target));
  BOOST_REQUIRE_SMALL(error, 0.00001);

  // Test the Backward function.
  module.Backward(std::move(input), std::move(target), std::move(output));
  // The output should be equal to 0.
  CheckMatrices(input, output);
  BOOST_REQUIRE_EQUAL(output.n_rows, input.n_rows);
  BOOST_REQUIRE_EQUAL(output.n_cols, input.n_cols);

  // Test the error function on a single input.
  input = arma::mat("2");
  target = arma::mat("3");
  error = module.Forward(std::move(input), std::move(target));
  BOOST_REQUIRE_CLOSE(error, 0.082760974810151655, 0.001);

  // Test the Backward function on a single input.
  module.Backward(std::move(input), std::move(target), std::move(output));
  BOOST_REQUIRE_CLOSE(arma::accu(output), -0.1917880483011872, 0.001);
  BOOST_REQUIRE_EQUAL(output.n_elem, 1);
}

/**
 * Test to check KL Divergence loss function when we take mean.
 */
BOOST_AUTO_TEST_CASE(KLDivergenceMeanTest)
{
  arma::mat input, target, output;
  double loss;
  KLDivergence<> module(true);

  // Test the Forward function.
  input = arma::mat("1 1 1 1 1 1 1 1 1 1");
  target = arma::exp(arma::mat("2 1 1 1 1 1 1 1 1 1"));

  loss = module.Forward(std::move(input), std::move(target));
  BOOST_REQUIRE_CLOSE_FRACTION(loss, -1.1 , 0.00001);

  // Test the Backward function.
  module.Backward(std::move(input), std::move(target), std::move(output));
  BOOST_REQUIRE_CLOSE_FRACTION(arma::as_scalar(output), -0.1, 0.00001);
}

/**
 * Test to check KL Divergence loss function when we do not take mean.
 */
BOOST_AUTO_TEST_CASE(KLDivergenceNoMeanTest)
{
  arma::mat input, target, output;
  double loss;
  KLDivergence<> module(false);

  // Test the Forward function.
  input = arma::mat("1 1 1 1 1 1 1 1 1 1");
  target = arma::exp(arma::mat("2 1 1 1 1 1 1 1 1 1"));

  loss = module.Forward(std::move(input), std::move(target));
  BOOST_REQUIRE_CLOSE_FRACTION(loss, -11, 0.00001);

  // Test the Backward function.
  module.Backward(std::move(input), std::move(target), std::move(output));
  BOOST_REQUIRE_CLOSE_FRACTION(arma::as_scalar(output), -1, 0.00001);
}

/*
 * Simple test for the mean squared error performance function.
 */
BOOST_AUTO_TEST_CASE(SimpleMeanSquaredErrorTest)
{
  arma::mat input, output, target;
  MeanSquaredError<> module;

  // Test the Forward function on a user generator input and compare it against
  // the manually calculated result.
  input = arma::mat("1.0 0.0 1.0 0.0 -1.0 0.0 -1.0 0.0");
  target = arma::zeros(1, 8);
  double error = module.Forward(std::move(input), std::move(target));
  BOOST_REQUIRE_EQUAL(error, 0.5);

  // Test the Backward function.
  module.Backward(std::move(input), std::move(target), std::move(output));
  // We subtract a zero vector, so according to the used backward formula:
  // output = 2 * (input - target) / target.n_cols,
  // output * nofColumns / 2 should be equal to input.
  CheckMatrices(input, output * output.n_cols / 2);
  BOOST_REQUIRE_EQUAL(output.n_rows, input.n_rows);
  BOOST_REQUIRE_EQUAL(output.n_cols, input.n_cols);

  // Test the error function on a single input.
  input = arma::mat("2");
  target = arma::mat("3");
  error = module.Forward(std::move(input), std::move(target));
  BOOST_REQUIRE_EQUAL(error, 1.0);

  // Test the Backward function on a single input.
  module.Backward(std::move(input), std::move(target), std::move(output));
  // Test whether the output is negative.
  BOOST_REQUIRE_EQUAL(arma::accu(output), -2);
  BOOST_REQUIRE_EQUAL(output.n_elem, 1);
}

/*
 * Simple test for the cross-entropy error performance function.
 */
BOOST_AUTO_TEST_CASE(SimpleCrossEntropyErrorTest)
{
  arma::mat input1, input2, output, target1, target2;
  CrossEntropyError<> module(1e-6);

  // Test the Forward function on a user generator input and compare it against
  // the manually calculated result.
  input1 = arma::mat("0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5");
  target1 = arma::zeros(1, 8);
  double error1 = module.Forward(std::move(input1), std::move(target1));
  BOOST_REQUIRE_SMALL(error1 - 8 * std::log(2), 2e-5);

  input2 = arma::mat("0 1 1 0 1 0 0 1");
  target2 = arma::mat("0 1 1 0 1 0 0 1");
  double error2 = module.Forward(std::move(input2), std::move(target2));
  BOOST_REQUIRE_SMALL(error2, 1e-5);

  // Test the Backward function.
  module.Backward(std::move(input1), std::move(target1), std::move(output));
  for (double el : output)
  {
    // For the 0.5 constant vector we should get 1 / (1 - 0.5) = 2 everywhere.
    BOOST_REQUIRE_SMALL(el - 2, 5e-6);
  }
  BOOST_REQUIRE_EQUAL(output.n_rows, input1.n_rows);
  BOOST_REQUIRE_EQUAL(output.n_cols, input1.n_cols);

  module.Backward(std::move(input2), std::move(target2), std::move(output));
  for (size_t i = 0; i < 8; ++i)
  {
    double el = output.at(0, i);
    if (input2.at(i) == 0)
      BOOST_REQUIRE_SMALL(el - 1, 2e-6);
    else
      BOOST_REQUIRE_SMALL(el + 1, 2e-6);
  }
  BOOST_REQUIRE_EQUAL(output.n_rows, input2.n_rows);
  BOOST_REQUIRE_EQUAL(output.n_cols, input2.n_cols);
}

/**
 * Simple test for the Sigmoid Cross Entropy performance function.
 */
BOOST_AUTO_TEST_CASE(SimpleSigmoidCrossEntropyErrorTest)
{
  arma::mat input1, input2, input3, output, target1,
            target2, target3, expectedOutput;
  SigmoidCrossEntropyError<> module;

  // Test the Forward function on a user generator input and compare it against
  // the calculated result.
  input1 = arma::mat("0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5");
  target1 = arma::zeros(1, 8);
  double error1 = module.Forward(std::move(input1), std::move(target1));
  double expected = 0.97407699;
  // Value computed using tensorflow.
  BOOST_REQUIRE_SMALL(error1 / input1.n_elem - expected, 1e-7);

  input2 = arma::mat("1 2 3 4 5");
  target2 = arma::mat("0 0 1 0 1");
  double error2 = module.Forward(std::move(input2), std::move(target2));
  expected = 1.5027283;
  BOOST_REQUIRE_SMALL(error2 / input2.n_elem - expected, 1e-6);

  input3 = arma::mat("0 -1 -1 0 -1 0 0 -1");
  target3 = arma::mat("0 -1 -1 0 -1 0 0 -1");
  double error3 = module.Forward(std::move(input3), std::move(target3));
  expected = 0.00320443;
  BOOST_REQUIRE_SMALL(error3 / input3.n_elem - expected, 1e-6);

  // Test the Backward function.
  module.Backward(std::move(input1), std::move(target1), std::move(output));
  expected = 0.62245929;
  for (size_t i = 0; i < output.n_elem; i++)
    BOOST_REQUIRE_SMALL(output(i) - expected, 1e-5);
  BOOST_REQUIRE_EQUAL(output.n_rows, input1.n_rows);
  BOOST_REQUIRE_EQUAL(output.n_cols, input1.n_cols);

  expectedOutput = arma::mat(
      "0.7310586 0.88079709 -0.04742587 0.98201376 -0.00669285");
  module.Backward(std::move(input2), std::move(target2), std::move(output));
  for (size_t i = 0; i < output.n_elem; i++)
    BOOST_REQUIRE_SMALL(output(i) - expectedOutput(i), 1e-5);
  BOOST_REQUIRE_EQUAL(output.n_rows, input2.n_rows);
  BOOST_REQUIRE_EQUAL(output.n_cols, input2.n_cols);

  module.Backward(std::move(input3), std::move(target3), std::move(output));
  expectedOutput = arma::mat("0.5 1.2689414");
  for (size_t i = 0; i < 8; ++i)
  {
    double el = output.at(0, i);
    if (std::abs(input3.at(i) - 0.0) < 1e-5)
      BOOST_REQUIRE_SMALL(el - expectedOutput[0], 2e-6);
    else
      BOOST_REQUIRE_SMALL(el - expectedOutput[1], 2e-6);
  }
  BOOST_REQUIRE_EQUAL(output.n_rows, input3.n_rows);
  BOOST_REQUIRE_EQUAL(output.n_cols, input3.n_cols);
}

/**
 * Simple test for the Earth Mover Distance Layer.
 */
BOOST_AUTO_TEST_CASE(SimpleEarthMoverDistanceLayerTest)
{
  arma::mat input1, input2, output, target1, target2, expectedOutput;
  EarthMoverDistance<> module;

  // Test the Forward function on a user generator input and compare it against
  // the manually calculated result.
  input1 = arma::mat("0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5");
  target1 = arma::zeros(1, 8);
  double error1 = module.Forward(std::move(input1), std::move(target1));
  double expected = 0.0;
  BOOST_REQUIRE_SMALL(error1 / input1.n_elem - expected, 1e-7);

  input2 = arma::mat("1 2 3 4 5");
  target2 = arma::mat("1 0 1 0 1");
  double error2 = module.Forward(std::move(input2), std::move(target2));
  expected = -1.8;
  BOOST_REQUIRE_SMALL(error2 / input2.n_elem - expected, 1e-6);

  // Test the Backward function.
  module.Backward(std::move(input1), std::move(target1), std::move(output));
  expected = 0.0;
  for (size_t i = 0; i < output.n_elem; i++)
    BOOST_REQUIRE_SMALL(output(i) - expected, 1e-5);
  BOOST_REQUIRE_EQUAL(output.n_rows, input1.n_rows);
  BOOST_REQUIRE_EQUAL(output.n_cols, input1.n_cols);

  expectedOutput = arma::mat("-1 0 -1 0 -1");
  module.Backward(std::move(input2), std::move(target2), std::move(output));
  for (size_t i = 0; i < output.n_elem; i++)
    BOOST_REQUIRE_SMALL(output(i) - expectedOutput(i), 1e-5);
  BOOST_REQUIRE_EQUAL(output.n_rows, input2.n_rows);
  BOOST_REQUIRE_EQUAL(output.n_cols, input2.n_cols);
}

/*
 * Mean Squared Error numerical gradient test.
 */
BOOST_AUTO_TEST_CASE(GradientMeanSquaredErrorTest)
{
  // Linear function gradient instantiation.
  struct GradientFunction
  {
    GradientFunction()
    {
      input = arma::randu(10, 1);
      target = arma::randu(2, 1);

      model = new FFN<MeanSquaredError<>, NguyenWidrowInitialization>();
      model->Predictors() = input;
      model->Responses() = target;
      model->Add<IdentityLayer<> >();
      model->Add<Linear<> >(10, 2);
      model->Add<SigmoidLayer<> >();
    }

    ~GradientFunction()
    {
      delete model;
    }

    double Gradient(arma::mat& gradient) const
    {
      arma::mat output;
      double error = model->Evaluate(model->Parameters(), 0, 1);
      model->Gradient(model->Parameters(), 0, gradient, 1);
      return error;
    }

    arma::mat& Parameters() { return model->Parameters(); }

    FFN<MeanSquaredError<>, NguyenWidrowInitialization>* model;
    arma::mat input, target;
  } function;

  BOOST_REQUIRE_LE(CheckGradient(function), 1e-4);
}

/*
 * Reconstruction Loss numerical gradient test.
 */
BOOST_AUTO_TEST_CASE(GradientReconstructionLossTest)
{
  // Linear function gradient instantiation.
  struct GradientFunction
  {
    GradientFunction()
    {
      input = arma::randu(10, 1);
      target = arma::randu(2, 1);

      model = new FFN<ReconstructionLoss<>, NguyenWidrowInitialization>();
      model->Predictors() = input;
      model->Responses() = target;
      model->Add<IdentityLayer<> >();
      model->Add<Linear<> >(10, 2);
      model->Add<SigmoidLayer<> >();
    }

    ~GradientFunction()
    {
      delete model;
    }

    double Gradient(arma::mat& gradient) const
    {
      arma::mat output;
      double error = model->Evaluate(model->Parameters(), 0, 1);
      model->Gradient(model->Parameters(), 0, gradient, 1);
      return error;
    }

    arma::mat& Parameters() { return model->Parameters(); }

    FFN<ReconstructionLoss<>, NguyenWidrowInitialization>* model;
    arma::mat input, target;
  } function;

  BOOST_REQUIRE_LE(CheckGradient(function), 1e-4);
}

/*
 * Simple test for the dice loss function.
 */
BOOST_AUTO_TEST_CASE(DiceLossTest)
{
  arma::mat input1, input2, target, output;
  double loss;
  DiceLoss<> module;

  // Test the Forward function. Loss should be 0 if input = target.
  input1 = arma::ones(10, 1);
  target = arma::ones(10, 1);
  loss = module.Forward(std::move(input1), std::move(target));
  BOOST_REQUIRE_SMALL(loss, 0.00001);

  // Test the Forward function. Loss should be 0.185185185.
  input2 = arma::ones(10, 1) * 0.5;
  loss = module.Forward(std::move(input2), std::move(target));
  BOOST_REQUIRE_CLOSE(loss, 0.185185185, 0.00001);

  // Test the Backward function for input = target.
  module.Backward(std::move(input1), std::move(target), std::move(output));
  for (double el : output)
  {
    // For input = target we should get 0.0 everywhere.
    BOOST_REQUIRE_CLOSE(el, 0.0, 0.00001);
  }
  BOOST_REQUIRE_EQUAL(output.n_rows, input1.n_rows);
  BOOST_REQUIRE_EQUAL(output.n_cols, input1.n_cols);

  // Test the Backward function.
  module.Backward(std::move(input2), std::move(target), std::move(output));
  for (double el : output)
  {
    // For the 0.5 constant vector we should get -0.0877914951989026 everywhere.
    BOOST_REQUIRE_CLOSE(el, -0.0877914951989026, 0.00001);
  }
  BOOST_REQUIRE_EQUAL(output.n_rows, input2.n_rows);
  BOOST_REQUIRE_EQUAL(output.n_cols, input2.n_cols);
}

/*
 * Simple test for the mean bias error performance function.
 */
BOOST_AUTO_TEST_CASE(SimpleMeanBiasErrorTest)
{
  arma::mat input, output, target;
  MeanBiasError<> module;

  // Test the Forward function on a user generator input and compare it against
  // the manually calculated result.
  input = arma::mat("1.0 0.0 1.0 -1.0 -1.0 0.0 -1.0 0.0");
  target = arma::zeros(1, 8);
  double error = module.Forward(std::move(input), std::move(target));
  BOOST_REQUIRE_EQUAL(error, 0.125);

  // Test the Backward function.
  module.Backward(std::move(input), std::move(target), std::move(output));
  // We should get a vector with -1 everywhere.
  for (double el : output)
  {
    BOOST_REQUIRE_EQUAL(el, -1);
  }
  BOOST_REQUIRE_EQUAL(output.n_rows, input.n_rows);
  BOOST_REQUIRE_EQUAL(output.n_cols, input.n_cols);

  // Test the error function on a single input.
  input = arma::mat("2");
  target = arma::mat("3");
  error = module.Forward(std::move(input), std::move(target));
  BOOST_REQUIRE_EQUAL(error, 1.0);

  // Test the Backward function on a single input.
  module.Backward(std::move(input), std::move(target), std::move(output));
  // Test whether the output is negative.
  BOOST_REQUIRE_EQUAL(arma::accu(output), -1);
  BOOST_REQUIRE_EQUAL(output.n_elem, 1);
}

/**
 * Simple test for the Log-Hyperbolic-Cosine loss function.
 */
BOOST_AUTO_TEST_CASE(LogCoshLossTest)
{
  arma::mat input, target, output;
  double loss;
  LogCoshLoss<> module(2);

  // Test the Forward function. Loss should be 0 if input = target.
  input = arma::ones(10, 1);
  target = arma::ones(10, 1);
  loss = module.Forward(std::move(input), std::move(target));
  BOOST_REQUIRE_EQUAL(loss, 0);

  // Test the Backward function for input = target.
  module.Backward(std::move(input), std::move(target), std::move(output));
  for (double el : output)
  {
    // For input = target we should get 0.0 everywhere.
    BOOST_REQUIRE_CLOSE(el, 0.0, 1e-5);
  }

  BOOST_REQUIRE_EQUAL(output.n_rows, input.n_rows);
  BOOST_REQUIRE_EQUAL(output.n_cols, input.n_cols);

  // Test the Forward function. Loss should be 0.546621.
  input = arma::mat("1 2 3 4 5");
  target = arma::mat("1 2.4 3.4 4.2 5.5");
  loss = module.Forward(std::move(input), std::move(target));
  BOOST_REQUIRE_CLOSE(loss, 0.546621, 1e-3);

  // Test the Backward function.
  module.Backward(std::move(input), std::move(target), std::move(output));
  BOOST_REQUIRE_CLOSE(arma::accu(output), 2.46962, 1e-3);
  BOOST_REQUIRE_EQUAL(output.n_rows, input.n_rows);
  BOOST_REQUIRE_EQUAL(output.n_cols, input.n_cols);
}
BOOST_AUTO_TEST_SUITE_END();


#include <mass.h>
#include <cmath>

#define BOOST_AUTO_TEST_MAIN
#include <boost/test/auto_unit_test.hpp>
#include <boost/test/unit_test_suite.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <boost/test/test_tools.hpp>

BOOST_AUTO_TEST_SUITE(mass);

BOOST_AUTO_TEST_CASE(cone_bounding_test)
{
  // Values for outer bounding cylinder -- given by bottom radius
  mass::Properties<float> O;

  // Values for inner bounding cylinder -- given by bottom radius
  ;

  // Values for conical solid
  ;

  float const rho = 1.0f;  // density
  float       a   = 2.0f;  // bottom radius
  float const b   = 1.0f;  // top radius
  float const h   = 1.0f;  // height

  // Compute inner bounding cylinder -- Never changes!!!
	mass::Properties<float> I = compute_cylinder(rho, b, h/2.0f);
  I = translate_to_model_frame(0.0f, h/2.0f, 0.0f, I);

  float old_Cy = 0.0f;

  for ( int i = 0 ; i < 9 ;++i)
  {
    a -= 0.1f;  // We are schrinking bottom radius of conical solid

    // Compute outer bounding cylinder
    mass::Properties<float> O = compute_cylinder(rho, a, h/2.0f);
    O = translate_to_model_frame(0.0f, h/2.0f, 0.0f, O);

    mass::Properties<float> C = compute_conical_solid(rho, a, b, h );

    BOOST_CHECK( !C.is_body_space() );
    BOOST_CHECK(  C.is_model_space() );

    // Verify that the mass are bounded by the cylinders
    BOOST_CHECK( I.m_m < C.m_m  );
    BOOST_CHECK( C.m_m < O.m_m  );

    // Verify that the xx inertia products are bounded
    BOOST_CHECK( I.m_Ixx < C.m_Ixx );
    BOOST_CHECK( C.m_Ixx < O.m_Ixx );

    // Verify that the yy inertia products are bounded
    BOOST_CHECK( I.m_Iyy < C.m_Iyy );
    BOOST_CHECK( C.m_Iyy < O.m_Iyy );

    // Verify that the zz inertia products are the same as teh xx values
    BOOST_CHECK_CLOSE(C.m_Ixx, C.m_Izz, 0.01f);

    // Test if center of mass in x-z plane are correct
    BOOST_CHECK_CLOSE(C.m_x, 0.0f, 0.01f);
    BOOST_CHECK_CLOSE(C.m_z, 0.0f, 0.01f);

    // Test if center of mass of conical is lower than the cylinders
    BOOST_CHECK(C.m_y < O.m_y);

    // As bottom radius is increas center of mass should rise up moving closer and closer towards center of mass of the boudning cylinders.
    BOOST_CHECK(C.m_y > old_Cy);
    old_Cy = C.m_y;
  }

}

BOOST_AUTO_TEST_SUITE_END();


#ifndef STAN_MATH_PRIM_SCAL_FUN_LOG_DIFF_EXP_HPP
#define STAN_MATH_PRIM_SCAL_FUN_LOG_DIFF_EXP_HPP

#include <stan/math/prim/meta.hpp>
#include <stan/math/prim/scal/fun/log1m_exp.hpp>
#include <stan/math/prim/scal/fun/constants.hpp>
#include <boost/math/tools/promotion.hpp>

namespace stan {
namespace math {

/**
 * The natural logarithm of the difference of the natural exponentiation
 * of x and the natural exponentiation of y
 *
 * This function is only defined for x >= y
 *
 *
   \f[
   \mbox{log\_diff\_exp}(x, y) =
   \begin{cases}
     \textrm{NaN} & \mbox{if } x < y\\
     \ln(\exp(x)-\exp(y)) & \mbox{if } x \geq y \\[6pt]
     \textrm{NaN} & \mbox{if } x = \textrm{NaN or } y = \textrm{NaN}
   \end{cases}
   \f]

   \f[
   \frac{\partial\, \mbox{log\_diff\_exp}(x, y)}{\partial x} =
   \begin{cases}
     \textrm{NaN} & \mbox{if } x \leq y\\
     \frac{\exp(x)}{\exp(x)-\exp(y)} & \mbox{if } x > y \\[6pt]
     \textrm{NaN} & \mbox{if } x = \textrm{NaN or } y = \textrm{NaN}
   \end{cases}
   \f]

   \f[
   \frac{\partial\, \mbox{log\_diff\_exp}(x, y)}{\partial y} =
   \begin{cases}
     \textrm{NaN} & \mbox{if } x \leq y\\
     -\frac{\exp(y)}{\exp(x)-\exp(y)} & \mbox{if } x > y \\[6pt]
     \textrm{NaN} & \mbox{if } x = \textrm{NaN or } y = \textrm{NaN}
   \end{cases}
   \f]
 *
 */
template <typename T1, typename T2>
inline return_type_t<T1, T2> log_diff_exp(const T1 x, const T2 y) {
  if (x <= y)
    return (x < INFTY && x == y) ? NEGATIVE_INFTY : NOT_A_NUMBER;
  return x + log1m_exp(y - x);
}

}  // namespace math
}  // namespace stan

#endif