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

#ifndef BOOST_ACCUMULATORS_STATISTICS_WEIGHTED_EXTENDED_P_SQUARE_HPP_DE_01_01_2006
#define BOOST_ACCUMULATORS_STATISTICS_WEIGHTED_EXTENDED_P_SQUARE_HPP_DE_01_01_2006

#include <vector>
#include <functional>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/iterator/counting_iterator.hpp>
#include <boost/iterator/permutation_iterator.hpp>
#include <boost/parameter/keyword.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/accumulators/framework/accumulator_base.hpp>
#include <boost/accumulators/framework/extractor.hpp>
#include <boost/accumulators/numeric/functional.hpp>
#include <boost/accumulators/framework/parameters/sample.hpp>
#include <boost/accumulators/framework/depends_on.hpp>
#include <boost/accumulators/statistics_fwd.hpp>
#include <boost/accumulators/statistics/count.hpp>
#include <boost/accumulators/statistics/sum.hpp>
#include <boost/accumulators/statistics/times2_iterator.hpp>
#include <boost/accumulators/statistics/extended_p_square.hpp>

namespace boost { namespace accumulators
{

namespace impl
{
    ///////////////////////////////////////////////////////////////////////////////
    // weighted_extended_p_square_impl
    //  multiple quantile estimation with weighted samples
    /**
        @brief Multiple quantile estimation with the extended \f$P^2\f$ algorithm for weighted samples

        This version of the extended \f$P^2\f$ algorithm extends the extended \f$P^2\f$ algorithm to
        support weighted samples. The extended \f$P^2\f$ algorithm dynamically estimates several
        quantiles without storing samples. Assume that \f$m\f$ quantiles
        \f$\xi_{p_1}, \ldots, \xi_{p_m}\f$ are to be estimated. Instead of storing the whole sample
        cumulative distribution, the algorithm maintains only \f$m+2\f$ principal markers and
        \f$m+1\f$ middle markers, whose positions are updated with each sample and whose heights
        are adjusted (if necessary) using a piecewise-parablic formula. The heights of the principal
        markers are the current estimates of the quantiles and are returned as an iterator range.

        For further details, see

        K. E. E. Raatikainen, Simultaneous estimation of several quantiles, Simulation, Volume 49,
        Number 4 (October), 1986, p. 159-164.

        The extended \f$ P^2 \f$ algorithm generalizes the \f$ P^2 \f$ algorithm of

        R. Jain and I. Chlamtac, The P^2 algorithm for dynamic calculation of quantiles and
        histograms without storing observations, Communications of the ACM,
        Volume 28 (October), Number 10, 1985, p. 1076-1085.

        @param extended_p_square_probabilities A vector of quantile probabilities.
    */
    template<typename Sample, typename Weight>
    struct weighted_extended_p_square_impl
      : accumulator_base
    {
        typedef typename numeric::functional::multiplies<Sample, Weight>::result_type weighted_sample;
        typedef typename numeric::functional::average<weighted_sample, std::size_t>::result_type float_type;
        typedef std::vector<float_type> array_type;
        // for boost::result_of
        typedef iterator_range<
            detail::lvalue_index_iterator<
                permutation_iterator<
                    typename array_type::const_iterator
                  , detail::times2_iterator
                >
            >
        > result_type;

        template<typename Args>
        weighted_extended_p_square_impl(Args const &args)
          : probabilities(
                boost::begin(args[extended_p_square_probabilities])
              , boost::end(args[extended_p_square_probabilities])
            )
          , heights(2 * probabilities.size() + 3)
          , actual_positions(heights.size())
          , desired_positions(heights.size())
        {
        }

        template<typename Args>
        void operator ()(Args const &args)
        {
            std::size_t cnt = count(args);
            std::size_t sample_cell = 1; // k
            std::size_t num_quantiles = this->probabilities.size();

            // m+2 principal markers and m+1 middle markers
            std::size_t num_markers = 2 * num_quantiles + 3;

            // first accumulate num_markers samples
            if(cnt <= num_markers)
            {
                this->heights[cnt - 1] = args[sample];
                this->actual_positions[cnt - 1] = args[weight];

                // complete the initialization of heights (and actual_positions) by sorting
                if(cnt == num_markers)
                {
                    // TODO: we need to sort the initial samples (in heights) in ascending order and
                    // sort their weights (in actual_positions) the same way. The following lines do
                    // it, but there must be a better and more efficient way of doing this.
                    typename array_type::iterator it_begin, it_end, it_min;

                    it_begin = this->heights.begin();
                    it_end   = this->heights.end();

                    std::size_t pos = 0;

                    while (it_begin != it_end)
                    {
                        it_min = std::min_element(it_begin, it_end);
                        std::size_t d = std::distance(it_begin, it_min);
                        std::swap(*it_begin, *it_min);
                        std::swap(this->actual_positions[pos], this->actual_positions[pos + d]);
                        ++it_begin;
                        ++pos;
                    }

                    // calculate correct initial actual positions
                    for (std::size_t i = 1; i < num_markers; ++i)
                    {
                        actual_positions[i] += actual_positions[i - 1];
                    }
                }
            }
            else
            {
                if(args[sample] < this->heights[0])
                {
                    this->heights[0] = args[sample];
                    this->actual_positions[0] = args[weight];
                    sample_cell = 1;
                }
                else if(args[sample] >= this->heights[num_markers - 1])
                {
                    this->heights[num_markers - 1] = args[sample];
                    sample_cell = num_markers - 1;
                }
                else
                {
                    // find cell k = sample_cell such that heights[k-1] <= sample < heights[k]

                    typedef typename array_type::iterator iterator;
                    iterator it = std::upper_bound(
                        this->heights.begin()
                      , this->heights.end()
                      , args[sample]
                    );

                    sample_cell = std::distance(this->heights.begin(), it);
                }

                // update actual position of all markers above sample_cell
                for(std::size_t i = sample_cell; i < num_markers; ++i)
                {
                    this->actual_positions[i] += args[weight];
                }

                // compute desired positions
                {
                    this->desired_positions[0] = this->actual_positions[0];
                    this->desired_positions[num_markers - 1] = sum_of_weights(args);
                    this->desired_positions[1] = (sum_of_weights(args) - this->actual_positions[0]) * probabilities[0]
                                              / 2. + this->actual_positions[0];
                    this->desired_positions[num_markers - 2] = (sum_of_weights(args) - this->actual_positions[0])
                                                            * (probabilities[num_quantiles - 1] + 1.)
                                                            / 2. + this->actual_positions[0];

                    for (std::size_t i = 0; i < num_quantiles; ++i)
                    {
                        this->desired_positions[2 * i + 2] = (sum_of_weights(args) - this->actual_positions[0])
                                                          * probabilities[i] + this->actual_positions[0];
                    }

                    for (std::size_t i = 1; i < num_quantiles; ++i)
                    {
                        this->desired_positions[2 * i + 1] = (sum_of_weights(args) - this->actual_positions[0])
                                                      * (probabilities[i - 1] + probabilities[i])
                                                      / 2. + this->actual_positions[0];
                    }
                }

                // adjust heights and actual_positions of markers 1 to num_markers - 2 if necessary
                for (std::size_t i = 1; i <= num_markers - 2; ++i)
                {
                    // offset to desired position
                    float_type d = this->desired_positions[i] - this->actual_positions[i];

                    // offset to next position
                    float_type dp = this->actual_positions[i + 1] - this->actual_positions[i];

                    // offset to previous position
                    float_type dm = this->actual_positions[i - 1] - this->actual_positions[i];

                    // height ds
                    float_type hp = (this->heights[i + 1] - this->heights[i]) / dp;
                    float_type hm = (this->heights[i - 1] - this->heights[i]) / dm;

                    if((d >= 1 && dp > 1) || (d <= -1 && dm < -1))
                    {
                        short sign_d = static_cast<short>(d / std::abs(d));

                        float_type h = this->heights[i] + sign_d / (dp - dm) * ((sign_d - dm)*hp + (dp - sign_d) * hm);

                        // try adjusting heights[i] using p-squared formula
                        if(this->heights[i - 1] < h && h < this->heights[i + 1])
                        {
                            this->heights[i] = h;
                        }
                        else
                        {
                            // use linear formula
                            if(d > 0)
                            {
                                this->heights[i] += hp;
                            }
                            if(d < 0)
                            {
                                this->heights[i] -= hm;
                            }
                        }
                        this->actual_positions[i] += sign_d;
                    }
                }
            }
        }

        result_type result(dont_care) const
        {
            // for i in [1,probabilities.size()], return heights[i * 2]
            detail::times2_iterator idx_begin = detail::make_times2_iterator(1);
            detail::times2_iterator idx_end = detail::make_times2_iterator(this->probabilities.size() + 1);

            return result_type(
                make_permutation_iterator(this->heights.begin(), idx_begin)
              , make_permutation_iterator(this->heights.begin(), idx_end)
            );
        }

    private:
        array_type probabilities;         // the quantile probabilities
        array_type heights;               // q_i
        array_type actual_positions;      // n_i
        array_type desired_positions;     // d_i
    };

} // namespace impl

///////////////////////////////////////////////////////////////////////////////
// tag::weighted_extended_p_square
//
namespace tag
{
    struct weighted_extended_p_square
      : depends_on<count, sum_of_weights>
      , extended_p_square_probabilities
    {
        typedef accumulators::impl::weighted_extended_p_square_impl<mpl::_1, mpl::_2> impl;
    };
}

///////////////////////////////////////////////////////////////////////////////
// extract::weighted_extended_p_square
//
namespace extract
{
    extractor<tag::weighted_extended_p_square> const weighted_extended_p_square = {};

    BOOST_ACCUMULATORS_IGNORE_GLOBAL(weighted_extended_p_square)
}

using extract::weighted_extended_p_square;

}} // namespace boost::accumulators

#endif


//
// Created by beck on 24/9/18.
//
#include <stdio.h>
#include <limits>
#include <string.h>
#include <Eigen/Dense>
#include <ros/ros.h>
#include "kalman.h"
#include "geometry_msgs/Point32.h"
#include "sensor_msgs/PointCloud.h"

ros::Subscriber radar_cloud_sub;
ros::Publisher filtered_cloud_pub;

bool filter_initialized = false;

KalmanFilter kf;

void
initialize_radar_filter(double distance)
{
    int n = 2;
    int m = 1;

    double dt = 1.0 / 14; // 14Hz from the radar

    Eigen::MatrixXd A(n, n);
    Eigen::MatrixXd H(m, n);
    Eigen::MatrixXd Q(n, n);
    Eigen::MatrixXd R(m, m);
    Eigen::MatrixXd P(n, n);

    // constant velocity model
    A << 1, dt,
         0, 1;
    H << 1, 0;
    Q << 1, 0,
         0, 1;
    R << 0.01;
    P << 1, 0,
         0, 1;

    // initialize the filter
    KalmanFilter kf_init(dt, A, H, Q, R, P);
    Eigen::VectorXd x0(n);
    x0 << distance, 0;

    // create a prediction with Kalman filter
    kf_init.init(ros::Time::now().toSec(), x0);
    kf = kf_init;
	filter_initialized = true;
	ROS_INFO("initialized the radar kalman filter at %f", distance);
}

// change the ID to the sorted order
void
publish_message(const int id, const std_msgs::Header& header)
{
    geometry_msgs::Point32 point;

	point.x = kf.state()[0]; // estimated state
	point.y = kf.state()[1]; // estimated velocity
	point.z = id;

	sensor_msgs::PointCloud ptCloud;
	ptCloud.header = header;
	ptCloud.points.push_back(point);
	filtered_cloud_pub.publish(ptCloud);
}

void
radar_callback(const sensor_msgs::PointCloud::ConstPtr pc_ptr)
{
    int n = 2;
    int m = 1;
    int s = pc_ptr->points.size();
    int i;

    if (s > 0) {
        if (!filter_initialized) {
            double distance = 9.0;

            for ( i = 0; i < s; ++i) {
                double x_ = pc_ptr->points[i].x;
                if (x_ > 9 && x_ < 10)
                    distance = x_;
            }
            initialize_radar_filter(distance);
        }
        else
        {
            double minChiSquare = std::numeric_limits<double>::max();
            int chi_index = s;
            bool found_nearest_point = false;
			int id = 0;

            // compare the chi-square to select into one
            for ( i = 0; i < s; ++i) {
                Eigen::VectorXd z(m);
                z << pc_ptr->points[i].x;

                double chiSquare = kf.chiSquare(z);
                if (chiSquare < minChiSquare) {
                    minChiSquare = chiSquare;
                    chi_index = i;
                }
            }
            double residual = pc_ptr->points[chi_index].x - kf.state()[0];
            if (residual < 1 && residual > -1)
                found_nearest_point = true;

            if (found_nearest_point) {
                ROS_INFO("found the number at %d with X2 %f", chi_index, minChiSquare);

                kf.propagate();

                Eigen::VectorXd z_sel(m);
                z_sel << pc_ptr->points[chi_index].x;
                kf.update(z_sel);

                publish_message(id, pc_ptr->header);
            }
			else {
				ROS_INFO("radar does not track the obstacle");
				// kf.propagate();

                publish_message(id, pc_ptr->header);
			}
        }
    }
	else {
		ROS_INFO("no point available");
	}
}

int main(int argc, char **argv)
{
    ros::init(argc, argv, "radar_preprocess");
    ros::NodeHandle n = ros::NodeHandle("~");

    radar_cloud_sub
    = n.subscribe<sensor_msgs::PointCloud>("/inf24g/inf24radar",
                                            10,
                                            radar_callback);
    filtered_cloud_pub
    = n.advertise<sensor_msgs::PointCloud>("/filtered_radar", 10);

    ros::spin();

    return 0;
}


#include <iostream>
#include <Eigen/Dense>

//unaryExpr, Lambda Expression, function pointer ,in place update
double ramp(double x)
{
    if (x > 0)
        return x;
    else
        return 0;
}

void unaryExprExample()
{
    //unaryExpr is a const function so it will not give you the possibility to modify values in place
    Eigen::ArrayXd x = Eigen::ArrayXd::Random(5);
    std::cout<<x <<std::endl;
    x = x.unaryExpr([](double elem) // changed type of parameter
    {
        return elem < 0.0 ? 0.0 : 1.0; // return instead of assignment
    });
    std::cout<<x <<std::endl;
    std::cout << x.unaryExpr(std::ptr_fun(ramp)) << std::endl;

}

int main(int argc, char **argv)
{
    return 0;
}


/*
 * This file is part of the statismo library.
 *
 * Author: Christoph Jud (christoph.jud@unibas.ch)
 *
 * Copyright (c) 2015 University of Basel
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * Neither the name of the project's author nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
#include <boost/scoped_ptr.hpp>
#include <ctime>
#include <vector>

#include <Eigen/Geometry>

#include <vtkMath.h>
#include <vtkPoints.h>
#include <vtkPolyDataReader.h>
#include <vtkPolyDataWriter.h>
#include <vtkVersion.h>

#include "CommonTypes.h"
#include "DataManager.h"
#include "Domain.h"
#include "genericRepresenterTest.hxx"
#include "PCAModelBuilder.h"
#include "vtkStandardMeshRepresenter.h"

using namespace statismo;

typedef GenericRepresenterTest<vtkStandardMeshRepresenter> RepresenterTestType;

vtkPolyData* loadPolyData(const std::string& filename) {
    vtkPolyDataReader* reader = vtkPolyDataReader::New();
    reader->SetFileName(filename.c_str());
    reader->Update();
    vtkPolyData* pd = vtkPolyData::New();
    pd->ShallowCopy(reader->GetOutput());
    reader->Delete();
    return pd;
}

void writePolyData(vtkPolyData* pd, const std::string& filename) {
    vtkSmartPointer< vtkPolyDataWriter > writer = vtkSmartPointer< vtkPolyDataWriter >::New();
    writer->SetFileName(filename.c_str());
#if (VTK_MAJOR_VERSION == 5 )
    writer->SetInput(pd);
#else
    writer->SetInputData(pd);
#endif
    writer->Update();
}

vtkPolyData* ReducePoints(vtkPolyData* poly, unsigned num_points) {
    vtkPoints* points = vtkPoints::New();
    unsigned step = unsigned(std::ceil(double(poly->GetPoints()->GetNumberOfPoints())/double(num_points)));
    for(unsigned i=0; i<poly->GetPoints()->GetNumberOfPoints(); i+=step) {
        points->InsertNextPoint(poly->GetPoints()->GetPoint(i));
    }
    vtkPolyData* res = vtkPolyData::New();
    res->SetPoints(points);
    return res;
}

double CompareVectors(const VectorType& v1, const VectorType& v2) {
    return (v1-v2).norm();
}

double CompareMatrices(const MatrixType& m1, const MatrixType& m2) {
    return (m1-m2).norm();
}

/**
 * The test works as follows:
 *  - First a standard PCA model is built out of the sample hand-shapes of statismo
 *    It is ensured that the standard argument results in exactly the same model
 *    as if JacobiSVD is provided.
 *  - In the second test, a PCA model is built out of 5000 samples drawn from the
 *    previous built model. Here, the SelfAdjointEigenSolver is used, since there
 *    are more samples than variables.
 *
 * Arguments:
 *  - one can provide a number of points with which the samples should be subsampled (default is 100)
 *  - additionally, one can provide an output directory. If this is provided, the principal
 *    components of the model are sampled and stored in this directory. This is meant for visual inspection.
 */

int main(int argc, char** argv) {
    if (argc < 2) {
        std::cout << "Usage: " << argv[0] << " datadir " << "[number_of_points=100] [output_dir]" << std::endl;
        exit(EXIT_FAILURE);
    }
    std::string datadir = std::string(argv[1]);

    bool testsOk = true;

    // number of points which are used for building the model
    // the number of points is reduced since the SelfAdjointEigenSolver operates
    // on the full covariance matrix. So
    unsigned num_points = 100;
    if(argc==3) num_points = std::atoi(argv[2]);

    std::string output_dir = "";
    if(argc==4) output_dir = argv[3];

    std::vector<std::string> filenames;
    filenames.push_back(datadir+"/hand_polydata/hand-0.vtk");
    filenames.push_back(datadir+"/hand_polydata/hand-1.vtk");
    filenames.push_back(datadir+"/hand_polydata/hand-2.vtk");
    filenames.push_back(datadir+"/hand_polydata/hand-3.vtk");
    filenames.push_back(datadir+"/hand_polydata/hand-4.vtk");
    filenames.push_back(datadir+"/hand_polydata/hand-5.vtk");
    filenames.push_back(datadir+"/hand_polydata/hand-6.vtk");
    filenames.push_back(datadir+"/hand_polydata/hand-7.vtk");
    filenames.push_back(datadir+"/hand_polydata/hand-8.vtk");
    filenames.push_back(datadir+"/hand_polydata/hand-9.vtk");
    filenames.push_back(datadir+"/hand_polydata/hand-10.vtk");
    filenames.push_back(datadir+"/hand_polydata/hand-11.vtk");
    filenames.push_back(datadir+"/hand_polydata/hand-12.vtk");
    filenames.push_back(datadir+"/hand_polydata/hand-13.vtk");
    filenames.push_back(datadir+"/hand_polydata/hand-14.vtk");
    filenames.push_back(datadir+"/hand_polydata/hand-15.vtk");
    filenames.push_back(datadir+"/hand_polydata/hand-16.vtk");

    typedef vtkStandardMeshRepresenter RepresenterType;
    typedef statismo::DataManager<vtkPolyData> DataManagerType;
    typedef vtkStandardMeshRepresenter::PointType PointType;
    typedef vtkStandardMeshRepresenter::DomainType DomainType;
    typedef DomainType::DomainPointsListType DomainPointsListType;
    typedef statismo::StatisticalModel<vtkPolyData> StatisticalModelType;

    vtkPolyData* reference = loadPolyData(filenames[0]);
    reference = ReducePoints(reference, num_points);
    RepresenterType* representer = RepresenterType::Create(reference);

    boost::scoped_ptr<DataManagerType> dataManager(DataManagerType::Create(representer));

    std::vector<std::string>::const_iterator it = filenames.begin();
    for(; it!=filenames.end(); it++) {
        vtkPolyData* testDataset = loadPolyData((*it));
        testDataset = ReducePoints(testDataset, num_points);
        dataManager->AddDataset(testDataset, "dataset");
    }

    // ----------------------------------------------------------
    // First compute PCA model using standard JacobiSVD
    // ----------------------------------------------------------
    std::cout << "PCAModelBuilderWithSelfAdjointEigenSolverTest: \t" << "building PCA model with JacobiSVD... " << std::flush;
    std::clock_t begin = std::clock();
    double data_noise = 0;
    typedef statismo::PCAModelBuilder<vtkPolyData> PCAModelBuilderType;
    PCAModelBuilderType* pcaModelBuilder = PCAModelBuilderType::Create();
    StatisticalModelType* jacobiModel;

    // perform with standard argument
    jacobiModel = pcaModelBuilder->BuildNewModel(dataManager->GetData(),data_noise,false);
    VectorType variance1 = jacobiModel->GetPCAVarianceVector();
    MatrixType pcbasis1 = jacobiModel->GetPCABasisMatrix();

    // perform with providing JacobiSVD
    jacobiModel = pcaModelBuilder->BuildNewModel(dataManager->GetData(),data_noise,false, PCAModelBuilderType::JacobiSVD);

    VectorType variance2 = jacobiModel->GetPCAVarianceVector();
    MatrixType pcbasis2 = jacobiModel->GetPCABasisMatrix();

    if(CompareVectors(variance1, variance2)==0 && CompareMatrices(pcbasis1, pcbasis2) == 0) {
        std::clock_t end = std::clock();
        std::cout << " (" << double(end - begin) / CLOCKS_PER_SEC << " sec) \t\t[passed]" << std::endl;
    } else {
        std::cout << " \t[failed]" << std::endl;
        std::cout << "PCAModelBuilderWithSelfAdjointEigenSolverTest: \t\t\t" << "- something went wrong with the standard argument!" << std::endl;
        testsOk = false;
    }

    // ----------------------------------------------------------
    // Generate a lot of samples out of the JacobiSVD model
    // ----------------------------------------------------------
    boost::scoped_ptr<DataManagerType> dataManager2(DataManagerType::Create(representer));
    dataManager->AddDataset(reference, "ref");
    for(unsigned i=0; i<5000; i++) {
        std::stringstream ss;
        ss << "sample" << i;
        dataManager2->AddDataset(jacobiModel->DrawSample(), ss.str().c_str());
    }

    // ----------------------------------------------------------
    // Compute PCA model using the SelfAdjointEigenSolver
    // ----------------------------------------------------------
    std::cout << "PCAModelBuilderWithSelfAdjointEigenSolverTest: \t" << "building PCA model with SelfAdjointEigenSolver... " << std::flush;
    begin = std::clock();
    StatisticalModelType* saesModel = pcaModelBuilder->BuildNewModel(dataManager2->GetData(),data_noise,false, PCAModelBuilderType::SelfAdjointEigenSolver);
    std::clock_t end = std::clock();
    std::cout << " (" << double(end - begin) / CLOCKS_PER_SEC << " sec) \t[passed]" << std::endl;

    // ----------------------------------------------------------
    // Comparing the models
    // - compare each principal component
    // ----------------------------------------------------------
    std::cout << "PCAModelBuilderWithSelfAdjointEigenSolverTest: \t" << "comparing principal components... " << std::flush;
    begin = std::clock();
    double error = 0;
    for(unsigned i=0; i<std::min(jacobiModel->GetNumberOfPrincipalComponents(), saesModel->GetNumberOfPrincipalComponents()); i++) {
        VectorType coeff1 = VectorType::Zero(jacobiModel->GetNumberOfPrincipalComponents());
        coeff1[i] = 2;
        VectorType coeff2 = VectorType::Zero(saesModel->GetNumberOfPrincipalComponents());
        coeff2[i] = 2;

        // it might be, that the direction of the pc is in the opposite direction
        double equal_direction = CompareVectors(jacobiModel->DrawSampleVector(coeff1, false),
                                                saesModel->DrawSampleVector(coeff2, false));
        coeff2[i] = -2;
        double oppo_direction = CompareVectors(jacobiModel->DrawSampleVector(coeff1, false),
                                               saesModel->DrawSampleVector(coeff2, false));

        error += std::min(equal_direction, oppo_direction);

        if(output_dir.size() >0) {
            std::stringstream ss1;
            ss1 << output_dir << "/jacobi-" << i << ".vtk";
            writePolyData(jacobiModel->DrawSample(coeff1, false), ss1.str().c_str());

            std::stringstream ss2;
            ss2 << output_dir << "/saes-" << i << ".vtk";
            if(equal_direction<oppo_direction) coeff2[i] = 2;
            writePolyData(saesModel->DrawSample(coeff2, false), ss2.str().c_str());
        }
    }

    double threshold = 150;
    if(error < threshold) {
        std::clock_t end = std::clock();
        std::cout << " (" << double(end - begin) / CLOCKS_PER_SEC << " sec) \t\t\t[passed]" << std::endl;
    } else {
        std::cout << " \t[failed]" << std::endl;
        std::cout << "PCAModelBuilderWithSelfAdjointEigenSolverTest: \t" << "- pc error("<< error << ") exceeds threshold ("<< threshold <<")!" << std::endl;
        testsOk = false;
    }

    double var_error = 0;
    for(unsigned i=0; i<std::min(jacobiModel->GetNumberOfPrincipalComponents(), saesModel->GetNumberOfPrincipalComponents()); i++) {
        var_error += std::sqrt(std::pow(jacobiModel->GetPCAVarianceVector()[i] - saesModel->GetPCAVarianceVector()[i],2));
    }

    std::cout << "PCAModelBuilderWithSelfAdjointEigenSolverTest: \t" << "comparing variances... " << std::flush;
    double var_threshold = 250;
    if(var_error < var_threshold) {
        std::clock_t end = std::clock();
        std::cout << " (" << double(end - begin) / CLOCKS_PER_SEC << " sec) \t\t\t\t[passed]" << std::endl;
    } else {
        std::cout << " \t[failed]" << std::endl;
        std::cout << "PCAModelBuilderWithSelfAdjointEigenSolverTest: \t" << "- variance error("<< var_error << ") exceeds threshold ("<< var_threshold <<")!" << std::endl;
        testsOk = false;
    }

    if(output_dir.size() >0) {
        std::cout << "PCAModelBuilderWithSelfAdjointEigenSolverTest: \t" << "The results can be visually inspected in the directory " << output_dir << std::endl;
    }

    if (testsOk == true) {
        std::cout << "PCAModelBuilderWithSelfAdjointEigenSolverTest: \t" << "Summary - tests passed." << std::endl;
        return EXIT_SUCCESS;
    } else {
        std::cout << "PCAModelBuilderWithSelfAdjointEigenSolverTest: \t" << "Summary - tests failed." << std::endl;
        return EXIT_FAILURE;
    }

}


// Boost.Geometry (aka GGL, Generic Geometry Library)
// Unit Test

// Copyright (c) 2008-2011 Bruno Lalande, Paris, France.

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

#include <geometry_test_common.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <boost/geometry/geometries/adapted/boost_tuple.hpp>
#include <boost/geometry/algorithms/distance.hpp>
#include <boost/geometry/strategies/strategies.hpp>

#include <iostream>

BOOST_GEOMETRY_REGISTER_BOOST_TUPLE_CS(cs::cartesian)

template <typename P>
void test_all()
{
    P p1, p2;
    bg::distance(p1, p2);
}

int test_main(int, char* [])
{
    test_all<boost::tuple<float> >();
    test_all<boost::tuple<int, int> >();
    test_all<boost::tuple<double, double, double> >();
    test_all<boost::tuple<float, float, float, float> >();
    test_all<boost::tuple<float, float, float, float, float> >();

    return 0;
}


//==================================================================================================
/*!

  Copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#include <boost/simd/function/scalar/fpclassify.hpp>
#include <boost/simd/function/std.hpp>
#include <scalar_test.hpp>
#include <boost/simd/constant/inf.hpp>
#include <boost/simd/constant/minf.hpp>
#include <boost/simd/constant/nan.hpp>
#include <boost/simd/constant/one.hpp>
#include <boost/simd/constant/mone.hpp>
#include <boost/simd/constant/zero.hpp>
#include <boost/simd/constant/mzero.hpp>
#include <boost/simd/constant/half.hpp>
#include <boost/simd/constant/mhalf.hpp>
#include <boost/simd/constant/mindenormal.hpp>
#include <cmath>

STF_CASE_TPL (" fpclassify",  STF_IEEE_TYPES)
{
  namespace bs = boost::simd;
  namespace bd = boost::dispatch;
  using bs::fpclassify;

  using r_t = decltype(fpclassify(T()));

  // return type conformity test
  STF_TYPE_IS(r_t, bd::as_integer_t<T>);

  // specific values tests
#ifndef BOOST_SIMD_NO_INVALIDS
  STF_EQUAL(fpclassify(bs::Inf<T>()), FP_INFINITE);
  STF_EQUAL(fpclassify(bs::Minf<T>()), FP_INFINITE);
  STF_EQUAL(fpclassify(bs::Nan<T>()), FP_NAN);
#endif
  STF_EQUAL(fpclassify(bs::Half<T>()), FP_NORMAL);
  STF_EQUAL(fpclassify(bs::Mhalf<T>()), FP_NORMAL);
  STF_EQUAL(fpclassify(bs::Mone<T>()), FP_NORMAL);
  STF_EQUAL(fpclassify(bs::Mindenormal<T>()), FP_SUBNORMAL);
  STF_EQUAL(fpclassify(bs::Zero<T>()), FP_ZERO);
  STF_EQUAL(fpclassify(bs::Mzero<T>()), FP_ZERO);
}


#ifndef STAN_MATH_PRIM_MAT_FUN_REP_ROW_VECTOR_HPP
#define STAN_MATH_PRIM_MAT_FUN_REP_ROW_VECTOR_HPP

#include <boost/math/tools/promotion.hpp>
#include <stan/math/prim/scal/err/check_nonnegative.hpp>
#include <stan/math/prim/mat/fun/Eigen.hpp>

namespace stan {
  namespace math {

    template <typename T>
    inline Eigen::Matrix<typename boost::math::tools::promote_args<T>::type,
                         1, Eigen::Dynamic>
    rep_row_vector(const T& x, int m) {
      check_nonnegative("rep_row_vector", "m",  m);
      return Eigen::Matrix<typename boost::math::tools::promote_args<T>::type,
                           1, Eigen::Dynamic>::Constant(m, x);
    }

  }
}

#endif


//==============================================================================
//         Copyright 2003 - 2011   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
/*!
 * \file
**/
#ifndef BOOST_SIMD_CONSTANT_CONSTANTS_MZERO_HPP_INCLUDED
#define BOOST_SIMD_CONSTANT_CONSTANTS_MZERO_HPP_INCLUDED

#include <boost/simd/include/functor.hpp>
#include <boost/simd/sdk/constant/register.hpp>
#include <boost/simd/sdk/constant/constant.hpp>

/*!
 * \ingroup boost_simd_constant
 * \defgroup boost_simd_constant_mzero Mzero
 *
 * \par Description
 * Constant Mzero = -0
 * This is a special constant as it can be used and considered
 * identical to zero, except that for floating point numbers,
 * there is two different representation of zero with different bit of sign.\par
 * The existence of the sign can be used in special circumstances as
 * choosing between \f$\pm\infty\f$ instead of nan in computing 1/0.
 * \par
 * The sign of zero can be accessed through the is_negative and is_positive predicates.
 *
 * \par Header file
 *
 * \code
 * #include <nt2/include/functions/mzero.hpp>
 * \endcode
 *
 *
 * \synopsis
 *
 * \code
 * namespace boost::simd
 * {
 *   template <class T,class A0>
 *     meta::call<tag::mzero_(A0)>::type
 *     Mzero();
 * }
 * \endcode
 *
 *
 * \param T template parameter of Mzero
 *
 * \return type T value
 *
 *
**/

namespace boost { namespace simd
{
  namespace tag
  {
    /*!
     * \brief Define the tag Mzero of functor Mzero
     *        in namespace boost::simd::tag for toolbox boost.simd.constant
    **/
    BOOST_SIMD_CONSTANT_REGISTER( Mzero, double, 0
                                , 0x80000000, 0x8000000000000000ULL
                                );
  }

  BOOST_SIMD_CONSTANT_IMPLEMENTATION(boost::simd::tag::Mzero, Mzero)
} }

#include <boost/simd/sdk/constant/common.hpp>

#endif


//####### Test module for picsar_algo ####################################

//Define Module name
 #define BOOST_TEST_MODULE "utils/picsar_algo"

//Include Boost unit tests library & library for floating point comparison
#include <boost/test/unit_test.hpp>
#include <boost/test/tools/floating_point_comparison.hpp>

//Force the use of PICSAR implementation of upper_bound and lower_bound for debug purposes
#define PXRMP_PICSAR_UPPER_BOUND
#define PXRMP_PICSAR_LOWER_BOUND
#include <picsar_qed/utils/picsar_algo.hpp>

#include <array>
#include <algorithm>

using namespace picsar::multi_physics::utils;

//Tolerance for double precision calculations
const double double_tolerance = 1.0e-12;

//Tolerance for single precision calculations
const float float_tolerance = 1.0e-6;

//Templated tolerance
template <typename T>
T constexpr tolerance()
{
    if(std::is_same<T,float>::value)
        return float_tolerance;
    else
        return double_tolerance;
}

// ------------- Tests --------------

// ***Test upper_bound

BOOST_AUTO_TEST_CASE( picsar_upper_bound_1 )
{
    const auto arr = std::array<double,5>{0.0, 1.0, 2.0, 3.0, 4.0};

    for (const auto nn : std::vector<double>{-1.,0.,0.1,1,1.1,3.9,4.0,5.0}){
        BOOST_CHECK_EQUAL(
            picsar_upper_bound(arr.begin(), arr.end(), nn),
            std::upper_bound(arr.begin(), arr.end(), nn));
    }
}

// ***Test lower_bound

BOOST_AUTO_TEST_CASE( picsar_lower_bound_1 )
{
    const auto arr = std::array<double,5>{0.0, 1.0, 2.0, 3.0, 4.0};

    for (const auto nn : std::vector<double>{-1.,0.,0.1,1,1.1,3.9,4.0,5.0}){
        BOOST_CHECK_EQUAL(
            picsar_lower_bound(arr.begin(), arr.end(), nn),
            std::lower_bound(arr.begin(), arr.end(), nn));
    }
}

// ***Test upper_bound_functor

BOOST_AUTO_TEST_CASE( picsar_upper_bound_functor_1 )
{
    const auto arr = std::array<double,5>{0.0, 1.0, 2.0, 3.0, 4.0};

    for (const auto nn : std::vector<double>{-1.,0.,0.1,1,1.1,3.9,4.0,5.0}){
        BOOST_CHECK_EQUAL(
            picsar_upper_bound_functor(0, arr.size(), nn, [&](size_t i){
                return arr[i];}),
            std::distance(
                arr.begin(),
                std::upper_bound(arr.begin(), arr.end(), nn)));

    }
}

// ***Test lower_bound_functor

BOOST_AUTO_TEST_CASE( picsar_lower_bound_functor_1 )
{
    const auto arr = std::array<double,5>{0.0, 1.0, 2.0, 3.0, 4.0};

    for (const auto nn : std::vector<double>{-1.,0.,0.1,1,1.1,3.9,4.0,5.0}){
        BOOST_CHECK_EQUAL(
            picsar_lower_bound_functor(0, arr.size(), nn, [&](size_t i){
                return arr[i];}),
            std::distance(
                arr.begin(),
                std::lower_bound(arr.begin(), arr.end(), nn)));

    }
}

// ***Test linear_interp

template<typename RealType>
void check_linear_interp()
{
    const auto linear_func = [](RealType x){
        return static_cast<RealType>(11.3 - 7.9*x);
    };

    const RealType x0 = 3.1;
    const RealType x1 = 4.9;
    const RealType y0 = linear_func(x0);
    const RealType y1 = linear_func(x1);
    const RealType x = 4.1;
    const RealType yexp = linear_func(x);
    const RealType y = linear_interp(x0, x1, y0, y1, x);

    BOOST_CHECK_SMALL((y-yexp)/y, tolerance<RealType>());
}

BOOST_AUTO_TEST_CASE( picsar_linear_interp )
{
    check_linear_interp<double>();
    check_linear_interp<float>();
}

// *******************************

// ***Test bilinear_interp

template<typename RealType>
void check_bilinear_interp()
{
    const auto bilinear_func = [](RealType x, RealType y){
        return static_cast<RealType>(11.3 - 7.9*x + 5.0*y);
    };

    const RealType x0 = 3.1;
    const RealType x1 = 4.9;
    const RealType y0 = -1.9;
    const RealType y1 = -1.0;
    const RealType z00 = bilinear_func(x0,y0);
    const RealType z01 = bilinear_func(x0,y1);
    const RealType z10 = bilinear_func(x1,y0);
    const RealType z11 = bilinear_func(x1,y1);
    const RealType x = 4.1;
    const RealType y = -1.5;
    const RealType zexp = bilinear_func(x,y);
    const RealType z = bilinear_interp(
        x0, x1, y0, y1, z00, z01, z10, z11, x, y);

    BOOST_CHECK_SMALL((z-zexp)/z, tolerance<RealType>());
}

BOOST_AUTO_TEST_CASE( picsar_bilinear_interp )
{
    check_bilinear_interp<double>();
    check_bilinear_interp<float>();
}

// *******************************


///////////////////////////////////////////////////////////////
//  Copyright 2021 John Maddock. Distributed under the Boost
//  Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt
//

#ifdef _MSC_VER
#define _SCL_SECURE_NO_WARNINGS
#endif

#include <thread>
#include <boost/detail/lightweight_test.hpp>
#include <boost/array.hpp>
#include <boost/math/special_functions/relative_difference.hpp>
#include <boost/math/special_functions/gamma.hpp>
#include <boost/math/quadrature/tanh_sinh.hpp>
#include "test.hpp"

#if !defined(TEST_MPF) && !defined(TEST_MPFR) && !defined(TEST_MPFI) && !defined(TEST_MPC)
#define TEST_MPF
#define TEST_MPFR
#define TEST_MPFI
#define TEST_MPC

#ifdef _MSC_VER
#pragma message("CAUTION!!: No backend type specified so testing everything.... this will take some time!!")
#endif
#ifdef __GNUC__
#pragma warning "CAUTION!!: No backend type specified so testing everything.... this will take some time!!"
#endif

#endif

#include <boost/multiprecision/gmp.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/multiprecision/cpp_bin_float.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>

#if defined(TEST_MPFR)
#include <boost/multiprecision/mpfr.hpp>
#endif
#if defined(TEST_MPFI)
#include <boost/multiprecision/mpfi.hpp>
#endif
#if defined(TEST_MPC)
#include <boost/multiprecision/mpc.hpp>
#endif

template <class T>
T new_value()
{
   return T("0.1");
}

template <class Other>
Other make_other_big_value()
{
   if constexpr (std::numeric_limits<Other>::is_integer && !std::numeric_limits<Other>::is_bounded)
      return (Other(1) << 1000) + 1;
   else if constexpr (!std::numeric_limits<Other>::is_integer && std::numeric_limits<Other>::is_exact && (std::numeric_limits<Other>::max_exponent == std::numeric_limits<Other>::min_exponent))
   {
      using value_type = typename Other::value_type;
      return Other(1) / ((value_type(1) << 1000) + 1);
   }
   else
      return (std::numeric_limits<Other>::max)();
}

template <class T, class Other>
void test_mixed()
{
   T::thread_default_precision(10);
   T::thread_default_variable_precision_options(boost::multiprecision::variable_precision_options::preserve_target_precision);
   Other big_a(make_other_big_value<Other>()), big_b(make_other_big_value<Other>()), big_c(make_other_big_value<Other>()), big_d(make_other_big_value<Other>());

   T a(big_a);
   BOOST_CHECK_EQUAL(a.precision(), 10);
   T b(std::move(big_d));
   BOOST_CHECK_EQUAL(a.precision(), 10);
   if constexpr (std::is_assignable_v<T, Other>)
   {
      a = big_b;
      BOOST_CHECK_EQUAL(a.precision(), 10);
      b = std::move(big_c);
      BOOST_CHECK_EQUAL(a.precision(), 10);

      if constexpr (!std::is_assignable_v<Other, T>)
      {
         a = b + big_a;
         BOOST_CHECK_EQUAL(a.precision(), 10);
         a = b * big_a;
         BOOST_CHECK_EQUAL(a.precision(), 10);
         a = b - big_a;
         BOOST_CHECK_EQUAL(a.precision(), 10);
         a = b / big_a;
         BOOST_CHECK_EQUAL(a.precision(), 10);
         a += big_a;
         BOOST_CHECK_EQUAL(a.precision(), 10);
         a -= big_a;
         BOOST_CHECK_EQUAL(a.precision(), 10);
         a *= big_a;
         BOOST_CHECK_EQUAL(a.precision(), 10);
         a /= big_a;
         BOOST_CHECK_EQUAL(a.precision(), 10);
      }
   }
   if constexpr (!std::is_same_v<T, typename T::value_type>)
   {
      T cc(big_a, big_b);
      BOOST_CHECK_EQUAL(cc.precision(), 10);
      T dd(big_a, big_b, 55);
      BOOST_CHECK_EQUAL(dd.precision(), 55);
      T aa = new_value<T>();
      BOOST_CHECK_EQUAL(aa.precision(), 10);
      aa.assign(big_a);
      BOOST_CHECK_EQUAL(aa.precision(), 10);
      aa.assign(big_a, big_b);
      BOOST_CHECK_EQUAL(aa.precision(), 10);
      if constexpr (0 && std::is_constructible_v<T, Other, Other, int>)
      {
         aa.assign(big_a, big_b, 55);
         BOOST_CHECK_EQUAL(aa.precision(), 55);
      }
   }
   else
   {
      if constexpr (std::is_constructible_v<T, Other, int>)
      {
         T aa(big_a, 55);
         BOOST_CHECK_EQUAL(aa.precision(), 55);
         aa.precision(10);
         BOOST_CHECK_EQUAL(aa.precision(), 10);
         aa.assign(big_a);
         BOOST_CHECK_EQUAL(aa.precision(), 10);
         aa.assign(big_a, 55);
         BOOST_CHECK_EQUAL(aa.precision(), 55);
      }
      else
      {
         T aa;
         BOOST_CHECK_EQUAL(aa.precision(), 10);
         aa.assign(big_a);
         BOOST_CHECK_EQUAL(aa.precision(), 10);
      }
   }
}

template <class T>
void test()
{
   T::thread_default_precision(100);
   T::thread_default_variable_precision_options(boost::multiprecision::variable_precision_options::preserve_target_precision);

   T hp1("0.1"), hp2("0.3"), hp3("0.11"), hp4("0.1231");

   BOOST_CHECK_EQUAL(hp1.precision(), 100);
   BOOST_CHECK_EQUAL(hp2.precision(), 100);

   T::thread_default_precision(35);

   T a(hp1);
   BOOST_CHECK_EQUAL(a.precision(), 35);
   a = hp1;
   BOOST_CHECK_EQUAL(a.precision(), 35);
   a = std::move(hp1);
   BOOST_CHECK_EQUAL(a.precision(), 35);
   T b(std::move(hp2));
   BOOST_CHECK_EQUAL(b.precision(), 35);

   a = b + hp3;
   BOOST_CHECK_EQUAL(a.precision(), 35);
   a = hp3 * b;
   BOOST_CHECK_EQUAL(a.precision(), 35);
   a += hp3;
   BOOST_CHECK_EQUAL(a.precision(), 35);
   a *= hp4;
   BOOST_CHECK_EQUAL(a.precision(), 35);
   a -= b * hp3;
   BOOST_CHECK_EQUAL(a.precision(), 35);

   if constexpr (!std::is_same_v<T, typename T::value_type>)
   {
      //
      // If we have a component type: ie we are an interval or a complex number, then
      // operations involving the component type should match those of T:
      //
      using component_t = typename T::value_type;
      component_t::thread_default_precision(100);
      component_t::thread_default_variable_precision_options(boost::multiprecision::variable_precision_options::preserve_source_precision);

      component_t cp1("0.1"), cp2("0.3"), cp3("0.11"), cp4("0.1231");

      BOOST_CHECK_EQUAL(cp1.precision(), 100);
      BOOST_CHECK_EQUAL(cp2.precision(), 100);

      T::thread_default_precision(35);

      T aa(cp1);
      BOOST_CHECK_EQUAL(aa.precision(), 35);
      T cc(cp1, cp2);
      BOOST_CHECK_EQUAL(cc.precision(), 35);
      T dd(cp1, cp2, 20);
      BOOST_CHECK_EQUAL(dd.precision(), 20);
      aa = cp1;
      BOOST_CHECK_EQUAL(aa.precision(), 35);
      aa = std::move(cp1);
      BOOST_CHECK_EQUAL(aa.precision(), 35);
      T bb(std::move(cp2));
      BOOST_CHECK_EQUAL(bb.precision(), 35);

      aa = bb + cp3;
      BOOST_CHECK_EQUAL(aa.precision(), 35);
      aa = cp3 * bb;
      BOOST_CHECK_EQUAL(aa.precision(), 35);
      aa += cp3;
      BOOST_CHECK_EQUAL(aa.precision(), 35);
      aa -= cp3;
      BOOST_CHECK_EQUAL(aa.precision(), 35);
      aa *= cp4;
      BOOST_CHECK_EQUAL(aa.precision(), 35);
      aa /= cp4;
      BOOST_CHECK_EQUAL(aa.precision(), 35);
      aa -= bb * cp3;
      BOOST_CHECK_EQUAL(aa.precision(), 35);

      aa.assign(cp1);
      BOOST_CHECK_EQUAL(aa.precision(), 35);
      aa.assign(cp1, cp2);
      BOOST_CHECK_EQUAL(aa.precision(), 35);
      aa.assign(cp1, cp2, 20);
      BOOST_CHECK_EQUAL(aa.precision(), 20);
   }
   else
   {
      T aa(hp4, 20);
      BOOST_CHECK_EQUAL(aa.precision(), 20);
      aa.precision(35);
      aa.assign(hp4);
      BOOST_CHECK_EQUAL(aa.precision(), 35);
      aa.assign(hp4, 20);
      BOOST_CHECK_EQUAL(aa.precision(), 20);
   }

   test_mixed<T, char>();
   test_mixed<T, unsigned char>();
   test_mixed<T, signed char>();
   test_mixed<T, short>();
   test_mixed<T, unsigned short>();
   test_mixed<T, int>();
   test_mixed<T, unsigned int>();
   test_mixed<T, long>();
   test_mixed<T, unsigned long>();
   test_mixed<T, long long>();
   test_mixed<T, unsigned long long>();
   test_mixed<T, float>();
   test_mixed<T, double>();
   test_mixed<T, long double>();
   //
   // Test with other compatible multiprecision types:
   //
   test_mixed<T, boost::multiprecision::mpz_int>();
   test_mixed<T, boost::multiprecision::cpp_int>();
   test_mixed<T, boost::multiprecision::mpq_rational>();
   test_mixed<T, boost::multiprecision::cpp_rational>();
   test_mixed<T, boost::multiprecision::cpp_bin_float_100>();
   test_mixed<T, boost::multiprecision::cpp_dec_float_100>();
   test_mixed<T, boost::multiprecision::mpf_float_100>();
#if defined(TEST_MPFR) || defined(TEST_MPC) || defined(TEST_MPFI)
   test_mixed<T, boost::multiprecision::mpfr_float_100>();
#endif
}

int main()
{
#ifdef TEST_MPF
   test<boost::multiprecision::mpf_float>();
   test<boost::multiprecision::number<boost::multiprecision::gmp_float<0>, boost::multiprecision::et_off>>();
#endif
#ifdef TEST_MPFR
   test<boost::multiprecision::mpfr_float>();
   test<boost::multiprecision::number<boost::multiprecision::mpfr_float_backend<0>, boost::multiprecision::et_off> >();
#endif
#ifdef TEST_MPFI
   test<boost::multiprecision::mpfi_float>();
   test<boost::multiprecision::number<boost::multiprecision::mpfi_float_backend<0>, boost::multiprecision::et_off> >();
#endif
#ifdef TEST_MPC
   test<boost::multiprecision::mpc_complex>();
   test<boost::multiprecision::number<boost::multiprecision::mpc_complex_backend<0>, boost::multiprecision::et_off> >();
#endif
   return boost::report_errors();
}


//==================================================================================================
/*!

  Copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#include <boost/simd/function/scalar/twopower.hpp>
#include <scalar_test.hpp>
#include <boost/simd/detail/dispatch/meta/as_integer.hpp>
#include <boost/simd/constant/inf.hpp>
#include <boost/simd/constant/minf.hpp>
#include <boost/simd/constant/mone.hpp>
#include <boost/simd/constant/nan.hpp>
#include <boost/simd/constant/one.hpp>
#include <boost/simd/constant/zero.hpp>

STF_CASE_TPL (" twopower ui",  STF_UNSIGNED_INTEGRAL_TYPES)
{
  namespace bs = boost::simd;
  namespace bd = boost::dispatch;
  using bs::twopower;
  using r_t = decltype(twopower(T()));

  STF_TYPE_IS( r_t, T );

  // specific values tests
  STF_EQUAL(twopower(bs::Zero<T>()), bs::One<r_t>());

  for(unsigned int i=0; i < sizeof(T)*8 ; i++)
 {
   STF_EQUAL(twopower(T(i)), T(1) << i);
 }
} // end of test for unsigned_int_

STF_CASE_TPL (" twopowersi",  STF_SIGNED_INTEGRAL_TYPES)
{
  namespace bs = boost::simd;
  namespace bd = boost::dispatch;
  using bs::twopower;
  using r_t = decltype(twopower(T()));

  // return type conformity test
  STF_TYPE_IS( r_t, T );

  // specific values tests
  STF_EQUAL(twopower(bs::Zero<T>()), bs::One<r_t>());
  STF_EQUAL(twopower(bs::Mone<T>()), bs::Zero<r_t>());

  for(unsigned int i=0; i < sizeof(T)*8-1 ; i++)
  {
    STF_EQUAL(twopower(T(i)), T(1) << i);
  }

} // end of test for unsigned_int_


#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <iostream>

#include <boost/log/trivial.hpp>
#include <boost/algorithm/string.hpp>

#include "navigation.hpp"
#include "common.hpp"
#include "antenna.hpp"
#include "enums.h"

#include "eigenIncluder.hpp"

/** Compare two time tags (t2-t1)
* Returns julian day difference
*/
double timecomp(
	const double t1[6],		///< time tag in [YMDHMS]
	const double t2[6])		///< time tag in [YMDHMS]
{
	/* convert two time tags to julian day */
	double jd1 = ymdhms2jd(t1);
	double jd2 = ymdhms2jd(t2);

// 	BOOST_LOG_TRIVIAL(debug) << "time difference in julian day is "<< jd2-jd1;

	return jd2-jd1;
}

/* decode antenna field */
int decodef(char *p, int n, double *v)
{
	int i;
	for (i = 0; i < n; i++)
		v[i] = 0;

	for (i = 0, p = strtok(p," "); p && i < n; p = strtok(NULL, " "))
	{
		v[i] = atof(p) * 1E-3;
		i++;
	}
	return i;
}

pcvacs_t* findAntenna(
	string code,
	double tc[6],
	nav_t& nav)
{
// 	BOOST_LOG_TRIVIAL(debug)
// 	<< "Searching for " << type << ", " << code;

	auto it1 = nav.pcvMap.find(code);
	if (it1 == nav.pcvMap.end())
	{
		return nullptr;
	}

	auto& pcvTimeMap = it1->second;

	if (pcvTimeMap.size() == 0)
	{
		return nullptr;
	}

	GTime time = epoch2time(tc);

	auto it2 = pcvTimeMap.lower_bound(time);
	if (it2 == pcvTimeMap.end())
	{
		//just use the first chronologically, (last when sorted as they are) instead
		auto it3 = pcvTimeMap.rbegin();
		pcvacs_t& pcv = it3->second;

		return &pcv;
	}

	pcvacs_t& pcv = it2->second;

	return &pcv;
}

/* linear interpolate pcv ------------------------------------------------------
*
* args     :       double x1              I       x1 lower bound (degree)
*                  double x2              I       x2 upper bound (degree)
*                  double y1              I       y1 lower bound (m)
*                  double y2              I       y2 upper bound (m)
*                  double x               O       x current point (degree)
*
* return   :       interpolated pcv (m)
*----------------------------------------------------------------------------*/
double interp(double x1, double x2, double y1, double y2, double x)
{
#if (0)
	return (y2-y1)*(x-x1)/(x2-x1)+y1;
#endif
	return y2-(y2-y1)*(x2-x)/(x2-x1);
}

/* fetch rec pco ---------------------------------------------------------------
*
* args     :       const pcvacs_t *pc     I       antenna info
*                  const chat sys         I       satellite system
*                  const int freq         I       frequency 1 or 2
*                  double pco[3]          O       rec pco (m)
*
* return   :       none
*
* note     :       frequencies are sorted as GPS, GLONASS, Galileo and BeiDou
*----------------------------------------------------------------------------*/
void recpco(pcvacs_t *pc, int freq, Vector3d& pco)
{
	pcvacs_t& pcv = *pc;
	/* assign rec/sat pco */
	if (pcv.pcoMap.find((E_FType)freq) == pcv.pcoMap.end())		pco = Vector3d::Zero();
	else														pco = pcv.pcoMap[(E_FType) freq];
}

/* fetch rec pcv ---------------------------------------------------------------
*
* args     :       const pcvacs_t pc      I       antenna info
*                  const chat sys         I       satellite system
*                  const int freq         I       frequency 1, 2
*                  const double el        I       satellite elevation (degree)
*                  const double azi       I       azimuth (degree)
*                  double *pcv            O       rec pcv (m)
*
* return   :       none
*
* note     :       frequencies are sorted as GPS, GLONASS, Galileo and BeiDou
*----------------------------------------------------------------------------*/
void recpcv(
	pcvacs_t*	pc,
	int			freq,
	double		el,
	double		azi,
	double&		pcv)
{
	int		nz		= pc->nz;
	int		naz		= pc->naz;
	double	zen1	= pc->zenStart;
	double	dzen	= pc->zenDelta;
	double	dazi	= pc->aziDelta;
	double	zen		= 90 - el;

	if (pc->PCVMap1D.find(freq) == pc->PCVMap1D.end())
	{
		//frequency not found
		return;
	}
	auto& pcvMap1D = pc->PCVMap1D[freq];

	/* select zenith angle range */
	int zen_n;
	for (zen_n = 1; zen_n < nz; zen_n++)
	{
		if ((zen1 + dzen * zen_n) >= zen)
		{
			break;
		}
	}

	double xz1 = zen1 + dzen * (zen_n - 1);
	double xz2 = zen1 + dzen * (zen_n);

	if (naz == 0)
	{
		/* linear interpolate receiver pcv - non azimuth-dependent */
		/* interpolate */

		double	yz1 = pcvMap1D[zen_n - 1];		// lower bound
		double	yz2 = pcvMap1D[zen_n];			// upper bound
		pcv = interp(xz1, xz2, yz1, yz2, zen);
	}
	else
	{
		if (pc->PCVMap2D.find(freq) == pc->PCVMap2D.end())
		{
			//frequency not found
			return;
		}
		auto& pcvMap2D = pc->PCVMap2D[freq];

		/* bilinear interpolate receiver pcv - azimuth-dependent */
		/* select azimuth angle range */
		int az_n;
		for (az_n = 1; az_n < naz; az_n++)
		{
			if ((dazi * az_n) >= azi)
			{
				break;
			}
		}

		double xa1 = dazi * (az_n -1);
		double xa2 = dazi * (az_n);

		double yz3 = pcvMap2D[az_n-1]	[zen_n-1];		double yz1 = pcvMap2D[az_n-1]	[zen_n];
		double yz4 = pcvMap2D[az_n]		[zen_n-1];		double yz2 = pcvMap2D[az_n]		[zen_n];

		/* linear interpolation along zenith angle */
		double ya1	= interp(xz1, xz2, yz3, yz1, zen);
		double ya2 	= interp(xz1, xz2, yz4, yz2, zen);

		/* linear interpolation along azimuth angle */
		pcv	= interp(xa1, xa2, ya1, ya2, azi);
	}

	return;
}
//=============================================================================
// radomeNoneAntennaType = radome2none(antennaType)
//
//       e,g, "AOAD/M_T        JPLA" => "AOAD/M_T        NONE"
//
// Change the last four characters of antenna type to NONE
// This function is useful for when searching for an antenna model in ANTEX
//
// The IGS convention is to default to NONE for the radome if the calibration
// value is not available
//=============================================================================
//void radome2none(char *restrict antenna_type)
void radome2none(string& antenna_type)
{
	size_t length = antenna_type.size();
	if (length != 20)
	{
		printf("\n*** ERROR radome2none(): string length is less then 20 characters received %ld characters\n",length);
		return;
	}
	antenna_type.replace(length - 4, 4, "NONE");
}

map<string, E_FType> antexCodes =
{
	{"G01", L1    },
	{"G02", L2    },
	{"G05", L5    },
	{"R01", G1    },
	{"R02", G2    },
	{"E01", E1    },
	{"E05", E5A   },
	{"E07", E5B   },
	{"E08", E5AB  },
	{"E06", E6    },
	{"C01", E1    },
	{"C02", E2    },
	{"C07", E5B   },
	{"C06", E6    },
	{"J01", L1    },
	{"J02", L2    },
	{"J05", L5    },
	{"J06", LEX   },
	{"S01", L1    },
	{"S05", L5    }
};

/** Read antex file */
int readantexf(
	string	file,
	nav_t&	nav)
{
	int offset;
	int noazi_flag		= 0;
	int num_azi_rd		= 0;
	int new_antenna		= 0;
	int num_antennas	= 0;
	int irms			= 0;

	char tmp[10];
	char *p;

	FILE* fp = fopen(file.c_str(),"r");
	if (fp == nullptr)
	{
		BOOST_LOG_TRIVIAL(warning)
		<< "Warning: ANTEX file opening error";

		return 0;
	}

	const pcvacs_t pcv0 = {};
	pcvacs_t ds_pcv;

	E_FType	ft = FTYPE_NONE;

	char buff[512];
	while (fgets(buff, sizeof(buff), fp))
	{
		char* comment = buff + 60;

		if (irms)
			continue;
		/* Read in the ANTEX header information */
		if (strlen(buff) < 60 )								{	continue;	}
		if (strstr(comment, "ANTEX VERSION / SYST"))		{	continue;	}
		if (strstr(comment, "PCV TYPE / REFANT")) 			{	continue;	}
		if (strstr(comment, "COMMENT")) 					{	continue;	}
		if (strstr(comment, "END OF HEADER"))				{	continue;	}
		/* Read in specific Antenna information now */

		if (strstr(comment, "START OF ANTENNA"))
		{
			num_antennas++;
			ds_pcv		= pcv0;
			new_antenna	= 1;        /* flag for new antenna */

			continue;
		}

		if (!new_antenna)
		{
			continue;
		}

		if (strstr(comment, "METH / BY / # / DATE"))
		{
// 			int num_calibrated;
// 			char cal_method[20];
// 			char cal_agency[20];
// 			char cal_date[10];
// 			strncpy(cal_method,	buff,		20);/* Should be CHAMBER or FIELD or ROBOT or COPIED ot CONVERTED */
// 			cal_method[19] = '\0';
// 			strncpy(cal_agency, buff + 20,	20);
// 			cal_agency[19] = '\0';
// 			strncpy(tmp,		buff + 40,	10);
// 			num_calibrated = atoi(tmp);
// 			strncpy(cal_date,	buff + 50,	10);
// 			cal_date[9] = '\0';

			continue;
		}

		if (strstr(comment, "DAZI"))
		{
			strncpy(tmp,buff   ,8);tmp[8] = '\0';
			ds_pcv.aziDelta = atof(tmp);

			if (ds_pcv.aziDelta < 0.0001)	ds_pcv.naz = 0;
			else                     		ds_pcv.naz = (360 / ds_pcv.aziDelta) + 1;

			continue;
		}

		if (strstr(comment, "END OF ANTENNA"))
		{
			/* reset the flags for the next antenna */
			new_antenna	= 0;

			/* stack antenna pco and pcv */
			string id;
			string& satId = ds_pcv.code;
			if (satId.find_first_not_of(' ') == satId.npos)		{ id = ds_pcv.type;	}
			else												{ id = ds_pcv.code;	}

			boost::trim_right(id);

			GTime time = epoch2time(ds_pcv.tf);
			nav.pcvMap[id][time] = ds_pcv;

			continue;
		}

		if (strstr(comment, "TYPE / SERIAL NO"))
		{
			ds_pcv.type		.assign(buff,		20);
			ds_pcv.code		.assign(buff+20,	20);
			ds_pcv.svn		.assign(buff+40,	4);
			ds_pcv.cospar	.assign(buff+50,	10);

			continue;
		}

		if (strstr(comment, "ZEN1 / ZEN2 / DZEN"))
		{
			strncpy(tmp, buff,		8);	tmp[8] = '\0'; 	ds_pcv.zenStart	= atof(tmp);
			strncpy(tmp, buff+8,	7);	tmp[8] = '\0'; 	ds_pcv.zenStop	= atof(tmp);
			strncpy(tmp, buff+16,	7);	tmp[8] = '\0'; 	ds_pcv.zenDelta	= atof(tmp);

			ds_pcv.nz = (ds_pcv.zenStop - ds_pcv.zenStart) / ds_pcv.zenDelta + 1 ;

			continue;
		}

		if (strstr(comment, "# OF FREQUENCIES"))
		{
//			strncpy(tmp, buff,		8);
// 			tmp[8] = '\0';
// 			ds_pcv.nf		= atoi(tmp);

			continue;
		}

		if (strstr(comment, "VALID FROM"))
		{
			char valid_from[44];
			/* if (!str2time(buff,0,43,pcv.ts)) continue;*/
			strncpy(valid_from, buff, 43);
			valid_from[43] = '\0';
			p = strtok(valid_from, " ");
			int j = 0;
			while (p != NULL)
			{
				ds_pcv.tf[j] = (double) atoi(p);
				p = strtok(NULL, " ");
				j++;
			}

			continue;
		}

		if (strstr(comment, "VALID UNTIL"))
		{
			char valid_until[44];
			/* if (!str2time(buff,0,43,pcv.te)) continue;*/
			strncpy(valid_until, buff   ,43);
			valid_until[43] = '\0';
			p = strtok(valid_until, " ");
			int j = 0;
			while (p != nullptr)
			{
				ds_pcv.tu[j] = (double) atoi(p);
				p = strtok(NULL, " ");
				j++;
			}

			continue;
		}

		if (strstr(comment, "NORTH / EAST / UP"))
		{
			double neu[3];
			if (decodef(buff, 3, neu) < 3)
			{
				continue;
			}

			/* assign pco value in ENU */
			Vector3d& enu = ds_pcv.pcoMap[ft];
			enu[0] = neu[1];
			enu[1] = neu[0];
			enu[2] = neu[2];

			continue;
		}

		if (strstr(comment, "START OF FREQUENCY"))
		{
			num_azi_rd = 0;
			noazi_flag = 0;

			string antexFCode;
			antexFCode.assign(&buff[3], 3);

			ft = antexCodes[antexFCode];

			continue;
		}

		if (strstr(comment, "END OF FREQUENCY"))	{	noazi_flag	= 0;	continue;	}
		if (strstr(comment, "START OF FREQ RMS"))	{	irms		= 1;	continue;	}
		if (strstr(comment, "END OF FREQ RMS"))		{	irms		= 0;	continue;	}

		if (!irms && strstr(buff,"NOAZI"))
		{
			for (int i = 0; i < ds_pcv.nz; i++)
			{
				offset = i * 8 + 8;
				strncpy(tmp, buff + offset, 8);
				tmp[8]='\0';
				double pcv_val = atof(tmp);
				ds_pcv.PCVMap1D[ft]			.push_back(pcv_val * 1e-3);
			}
			noazi_flag = 1;

			continue;
		}

		if (!irms && noazi_flag == 1)
		{
			strncpy(tmp, buff, 8);
			tmp[8]='\0';

			for (int i = 0; i < ds_pcv.nz; i++)
			{
				offset = i * 8 + 8;
				strncpy(tmp, buff + offset, 8);
				tmp[8]='\0';
				double pcv_val = atof(tmp);
				ds_pcv.PCVMap2D[ft][num_azi_rd].push_back(pcv_val * 1e-3);
			}
			num_azi_rd++;

			continue;
		}
	}

	fclose(fp);

	return 1;
}

/** Satellite antenna model.
* Compute satellite antenna phase center parameters
*/
// inplace of antmodel_
// this will not work
// for galileo models
void interp_satantmodel(
	pcvacs_t&			pcv,	///< antenna phase center parameters
	double				nadir,	///< nadir angle for satellite (rad)
	map<int, double>&	dant)	///< range offsets for each frequency (m)
{
	for (auto& [ft, pcvVector] : pcv.PCVMap1D)
	{
		double	nadirDeg		= nadir * R2D;				// ang=0-90
		int		numSections		= pcvVector.size();
		double	startAngle		= pcv.zenStart;
		double	sectionWidth	= pcv.zenDelta;
		double	realSection		= ((nadirDeg - startAngle) / sectionWidth);
		double	intSection		= (int) realSection;
		double	fraction		= realSection - intSection;

		if		(intSection < 0)				{	dant[ft] = pcvVector[0];					}
		else if	(intSection >= numSections)		{	dant[ft] = pcvVector[numSections-1];		}
		else
		{
			double a = pcvVector[intSection];
			double b = pcvVector[intSection + 1];

			dant[ft] = a + fraction * (b - a);
		}
	}
}


#include <iostream>
#include <armadillo>

#include "sph/Solver.hpp"
#include "spdlog/spdlog.h"
#include "sph/Particle.hpp"
#include "solid/Box.hpp"

using namespace spdlog;
using namespace arma;

void gen_fluid(Solver& s, Particle& po) {
    auto r = po.rad;
    auto l = s.getDomainRange();
    int id = 0;
    VEC debut = -l + VEC{r, r, r };
    VEC end = l - VEC{r, r, l[0] };
    for (double i = debut[0]; i <= end[0]; i+=2*r) {
        for (double j = debut[1]; j <= end[1]; j+=2*r) {
            for (double k = debut[2]; k <= end[2]; k+=2*r) {
                id ++;
                Particle p(po);
                p.curr_pos = VEC({i, j, k});
                p.id = id;
                s.append(make_shared<Particle>(p));
            }
        }
    }
}

int main() {
    auto console = stdout_logger_mt("console");
    set_level(level::info);

    VEC v0 = {0., 1., 0.};
    VEC vr = {0., 0., 0.};
    VEC v1 = {1., 1., 1.};
    Box b1(0.2, SolidType::Recipient, v0, vr, v1);

    Solver s(0.001, 50, 1, 0.3, b1);

    shared_ptr<Fluid> f1 = make_shared<Fluid>(1000, 0.001, 75.64, 2, 0.1, 0.7);
    shared_ptr<listForces> lf = make_shared<listForces>(listForces{make_shared<Force>(VEC{0, 0, -9.8})});

    shared_ptr<KernelPoly6> kp6 = make_shared<KernelPoly6>(0.3);
    shared_ptr<KernelSpiky> ksp = make_shared<KernelSpiky>(0.3);
    shared_ptr<KernelViscosity> kv = make_shared<KernelViscosity>(0.3);

    Particle p1 = Particle(0.05, f1, lf, kp6, ksp, kv, kp6, 0);

    gen_fluid(s, p1);

    s.go();
}


#include <cctbx/math/cos_sin_table.h>
#include <iostream>
#include <boost/timer.hpp>

int main() {
  unsigned const n = 1024, p = 1024;
  cctbx::math::cos_sin_table<double> table(n);
  cctbx::math::cos_sin_exact<double> exact;
  std::complex<double> sum = 0;
  boost::timer t;
  for (unsigned i=0; i<p*n; ++i) {
    sum += table(i);
  }
  std::cout << t.elapsed() << "\t" << sum << std::endl;
  sum = 0;
  t.restart();
  for (unsigned i=0; i<p*n; ++i) {
    sum += exact(i);
  }
  std::cout << t.elapsed() << "\t" << sum << std::endl;
  std::cout << "OK\n" << std::endl;
  return 0;
}


#ifndef _NUMERIC_HPP_
#define _NUMERIC_HPP_

#include "config.hpp"
#include "Macros.hpp"
#include "Base.hpp"
#include <boost/shared_ptr.hpp>
#include <variant>
#include <type_traits>

using namespace boost;
using namespace log4cpp;

NAMESPACE_BEGIN(interp)

    template <class EncodingT>
    class Numeric
    : public Base<EncodingT>
    {
    private:
        typedef std::variant<long long,double> numeric_variant_t;
        numeric_variant_t m_value;

    public:
        // Constructor
        Numeric();

        Numeric(const numeric_variant_t& value);
        template <class T, class = typename std::enable_if<std::is_integral<T>::value||std::is_enum<T>::value,T>::type>
        Numeric(T value);
        template <class T, class = void, class = typename std::enable_if<std::is_floating_point<T>::value,T>::type>
        Numeric(T value);

        FACTORY_PROTOTYPE1(Numeric, In< boost::shared_ptr< Base<EncodingT> > >)
        Numeric(boost::shared_ptr< Base<EncodingT> > const& value);

        // Accessors
        double Dvalue() const;
        void Dvalue(double value);
        long long LLvalue() const;
        void LLvalue(long long value);
        const numeric_variant_t& value() const;
        void value(const numeric_variant_t& value);

        // Virtual methods
        virtual typename EncodingT::string_t toString() const;
        virtual boost::shared_ptr< Base<EncodingT> > clone() const;
        virtual typename EncodingT::string_t getClassName() const;
        virtual boost::shared_ptr< Base<EncodingT> > invoke(const typename EncodingT::string_t& method, std::vector< boost::shared_ptr< Base<EncodingT> > >& params);

        // Dynamic methods
        boost::shared_ptr< Base<EncodingT> > minus() const;

        FACTORY_PROTOTYPE1(plus, In< boost::shared_ptr< Base<EncodingT> > >)
        boost::shared_ptr< Base<EncodingT> > plus(boost::shared_ptr< Base<EncodingT> > const& val) const;

        FACTORY_PROTOTYPE1(minus, In< boost::shared_ptr< Base<EncodingT> > >)
        boost::shared_ptr< Base<EncodingT> > minus(boost::shared_ptr< Base<EncodingT> > const& val) const;

        FACTORY_PROTOTYPE1(multiply, In< boost::shared_ptr< Base<EncodingT> > >)
        boost::shared_ptr< Base<EncodingT> > multiply(boost::shared_ptr< Base<EncodingT> > const& val) const;

        FACTORY_PROTOTYPE1(divide, In< boost::shared_ptr< Base<EncodingT> > >)
        boost::shared_ptr< Base<EncodingT> > divide(boost::shared_ptr< Base<EncodingT> > const& val) const;

        FACTORY_PROTOTYPE1(equals, In< boost::shared_ptr< Base<EncodingT> > >)
        boost::shared_ptr< Base<EncodingT> > equals(boost::shared_ptr< Base<EncodingT> > const& val) const;

        FACTORY_PROTOTYPE1(notEquals, In< boost::shared_ptr< Base<EncodingT> > >)
        boost::shared_ptr< Base<EncodingT> > notEquals(boost::shared_ptr< Base<EncodingT> > const& val) const;

        FACTORY_PROTOTYPE1(inferior, In< boost::shared_ptr< Base<EncodingT> > >)
        boost::shared_ptr< Base<EncodingT> > inferior(boost::shared_ptr< Base<EncodingT> > const& val) const;

        FACTORY_PROTOTYPE1(inferiorOrEqual, In< boost::shared_ptr< Base<EncodingT> > >)
        boost::shared_ptr< Base<EncodingT> > inferiorOrEqual(boost::shared_ptr< Base<EncodingT> > const& val) const;

        FACTORY_PROTOTYPE1(superior, In< boost::shared_ptr< Base<EncodingT> > >)
        boost::shared_ptr< Base<EncodingT> > superior(boost::shared_ptr< Base<EncodingT> > const& val) const;

        FACTORY_PROTOTYPE1(superiorOrEqual, In< boost::shared_ptr< Base<EncodingT> > >)
        boost::shared_ptr< Base<EncodingT> > superiorOrEqual(boost::shared_ptr< Base<EncodingT> > const& val) const;

        void increment();

        void decrement();

        boost::shared_ptr< Base<EncodingT> > round() const;

        FACTORY_PROTOTYPE1(parse, In< boost::shared_ptr< Base<EncodingT> > >)
        void parse(boost::shared_ptr< Base<EncodingT> > const& str);

        FACTORY_PROTOTYPE1(parseHex, In< boost::shared_ptr< Base<EncodingT> > >)
        void parseHex(boost::shared_ptr< Base<EncodingT> > const& str);

        FACTORY_PROTOTYPE3(parseBase64, In< boost::shared_ptr< Base<EncodingT> > >, In< boost::shared_ptr< Base<EncodingT> > >, In< boost::shared_ptr< Base<EncodingT> > >)
        void parseBase64(boost::shared_ptr< Base<EncodingT> > const& str, boost::shared_ptr< Base<EncodingT> > const& pos, boost::shared_ptr< Base<EncodingT> > const& size);

        boost::shared_ptr< Base<EncodingT> > convertHex() const;

        // Methods registration
        FACTORY_BEGIN_REGISTER
            CLASS_REGISTER    (Numeric)
            CLASS_REGISTER1   (Numeric)
            METHOD_REGISTER   (Numeric, boost::shared_ptr< Base<EncodingT> >, minus, const_t)
            METHOD_REGISTER1  (Numeric, boost::shared_ptr< Base<EncodingT> >, plus, const_t)
            METHOD_REGISTER1  (Numeric, boost::shared_ptr< Base<EncodingT> >, minus, const_t)
            METHOD_REGISTER1  (Numeric, boost::shared_ptr< Base<EncodingT> >, multiply, const_t)
            METHOD_REGISTER1  (Numeric, boost::shared_ptr< Base<EncodingT> >, divide, const_t)
            METHOD_REGISTER1  (Numeric, boost::shared_ptr< Base<EncodingT> >, inferior, const_t)
            METHOD_REGISTER1  (Numeric, boost::shared_ptr< Base<EncodingT> >, inferiorOrEqual, const_t)
            METHOD_REGISTER1  (Numeric, boost::shared_ptr< Base<EncodingT> >, superior, const_t)
            METHOD_REGISTER1  (Numeric, boost::shared_ptr< Base<EncodingT> >, superiorOrEqual, const_t)
            METHOD_REGISTER1  (Numeric, boost::shared_ptr< Base<EncodingT> >, equals, const_t)
            METHOD_REGISTER1  (Numeric, boost::shared_ptr< Base<EncodingT> >, notEquals, const_t)
            METHOD_KEY_REGISTER  (Numeric, void, increment, no_const_t, UCS("Numeric::Increment"))
            METHOD_KEY_REGISTER  (Numeric, void, decrement, no_const_t, UCS("Numeric::Decrement"))
            METHOD_KEY_REGISTER  (Numeric, boost::shared_ptr< Base<EncodingT> >, round, const_t, UCS("Numeric::Round"))
            METHOD_KEY_REGISTER1 (Numeric, void, parse, no_const_t, UCS("Numeric::Parse"))
            METHOD_KEY_REGISTER1 (Numeric, void, parseHex, no_const_t, UCS("Numeric::ParseHex"))
            METHOD_KEY_REGISTER3 (Numeric, void, parseBase64, no_const_t, UCS("Numeric::ParseBase64"))
            METHOD_KEY_REGISTER  (Numeric, boost::shared_ptr< Base<EncodingT> >, convertHex, const_t, UCS("Numeric::ConvertHex"))
        FACTORY_END_REGISTER

        // Methods unregistration
        FACTORY_BEGIN_UNREGISTER
            CLASS_UNREGISTER  (Numeric)
            CLASS_UNREGISTER1 (Numeric)
            METHOD_UNREGISTER (Numeric, minus)
            METHOD_UNREGISTER1(Numeric, plus)
            METHOD_UNREGISTER1(Numeric, minus)
            METHOD_UNREGISTER1(Numeric, multiply)
            METHOD_UNREGISTER1(Numeric, divide)
            METHOD_UNREGISTER1(Numeric, inferior)
            METHOD_UNREGISTER1(Numeric, inferiorOrEqual)
            METHOD_UNREGISTER1(Numeric, superior)
            METHOD_UNREGISTER1(Numeric, superiorOrEqual)
            METHOD_UNREGISTER1(Numeric, equals)
            METHOD_UNREGISTER1(Numeric, notEquals)
            METHOD_KEY_UNREGISTER (UCS("Numeric::Increment"))
            METHOD_KEY_UNREGISTER (UCS("Numeric::Decrement"))
            METHOD_KEY_UNREGISTER (UCS("Numeric::Round"))
            METHOD_KEY_UNREGISTER1(UCS("Numeric::Parse"))
            METHOD_KEY_UNREGISTER1(UCS("Numeric::ParseHex"))
            METHOD_KEY_UNREGISTER3(UCS("Numeric::ParseBase64"))
            METHOD_KEY_UNREGISTER (UCS("Numeric::ConvertHex"))
        FACTORY_END_UNREGISTER
    };

    template <class EncodingT>
    bool check_numeric(boost::shared_ptr< Base<EncodingT> > const& val, std::variant<long long,double>& n);

    template <class EncodingT>
    bool reset_numeric(boost::shared_ptr< Base<EncodingT> >& val, std::variant<long long,double> const& n);

    template <class EncodingT, class T, class = typename std::enable_if<std::is_integral<T>::value||std::is_enum<T>::value>::type>
    bool check_numeric_i(boost::shared_ptr< Base<EncodingT> > const& val, T& n);

    template <class EncodingT, class T, class = typename std::enable_if<std::is_integral<T>::value||std::is_enum<T>::value>::type>
    bool reset_numeric_i(boost::shared_ptr< Base<EncodingT> >& val, T n);

    template <class EncodingT, class T, class = typename std::enable_if<std::is_floating_point<T>::value>::type>
    bool check_numeric_d(boost::shared_ptr< Base<EncodingT> > const& val, T& n);

    template <class EncodingT, class T, class = typename std::enable_if<std::is_floating_point<T>::value>::type>
    bool reset_numeric_d(boost::shared_ptr< Base<EncodingT> >& val, T n);

NAMESPACE_END

#include "Numeric_impl.hpp"

#endif


/* Hector -- A Simple Climate Model
   Copyright (C) 2014-2015  Battelle Memorial Institute

   Please see the accompanying file LICENSE.md for additional licensing
   information.
*/
 /*
 * carbon-cycle-solver.cpp
 *
 * ODE solver for integrating the carbon cycle in hector.
 *
 * See notes in carbon-cycle-solver.cpp
 *
 */

#include <math.h>
#include <string>
#include <boost/numeric/odeint.hpp>

#include "carbon-cycle-solver.hpp"
#include "avisitor.hpp"

namespace Hector {

//------------------------------------------------------------------------------
/*! \brief Constructor
 */
CarbonCycleSolver::CarbonCycleSolver() : nc( 0 ),
eps_abs( 1.0e-6 ),eps_rel( 1.0e-6 ),
dt( 0.3 )
{
}

//------------------------------------------------------------------------------
/*! \brief Deconstructor
 */
CarbonCycleSolver::~CarbonCycleSolver()
{
}

//------------------------------------------------------------------------------
// documentation is inherited
void CarbonCycleSolver::init( Core* coreptr ) {
    // This component is very verbose at the debug and notice levels, so limit
    // output to the warning level, even if the rest of the model is configured
    // for something lower.
    logger.open(getComponentName(), false, coreptr->getGlobalLogger().getEchoToFile(), Logger::WARNING);
    H_LOG( logger, Logger::DEBUG ) << getComponentName() << " initialized." << std::endl;

    core = coreptr;

    in_spinup = false;

    // We want to run after the carbon box models, to give them a chance to initialize
    core->registerDependency( D_ATMOSPHERIC_C, getComponentName() );
}

//------------------------------------------------------------------------------
// documentation is inherited
unitval CarbonCycleSolver::sendMessage( const std::string& message,
                                       const std::string& datum,
                                       const message_data info ) throw ( h_exception )
{
    unitval returnval;

    if( message==M_GETDATA ) {          //! Caller is requesting data
        return getData( datum, info.date );

    } else if( message==M_SETDATA ) {   //! Caller is requesting to set data
        //TODO: call setData below
        //TODO: change core so that parsing is routed through sendMessage
        //TODO: make setData private

    } else {                        //! We don't handle any other messages
        H_THROW( "Caller sent unknown message: "+message );
    }

    return returnval;
}

//------------------------------------------------------------------------------
// documentation is inherited
void CarbonCycleSolver::setData( const std::string &varName,
                                 const message_data& data ) throw ( h_exception )
{
    H_LOG( logger, Logger::DEBUG ) << "Setting " << varName << "[" << data.date << "]=" << data.value_str << std::endl;

    try {
        if( varName == D_CCS_EPS_ABS ) {
            H_ASSERT( data.date == Core::undefinedIndex() , "date not allowed" );
            eps_abs = data.getUnitval(U_UNDEFINED);;
        }
        else if( varName == D_CCS_EPS_REL ) {
            H_ASSERT( data.date == Core::undefinedIndex() , "date not allowed" );
            eps_rel = data.getUnitval(U_UNDEFINED);;
        }
        else if( varName == D_CCS_DT ) {
            H_ASSERT( data.date == Core::undefinedIndex() , "date not allowed" );
            dt = data.getUnitval(U_UNDEFINED);
        }
        else if( varName == D_EPS_SPINUP ) {
            H_ASSERT( data.date == Core::undefinedIndex() , "date not allowed" );
            eps_spinup = data.getUnitval(U_PGC);
        }
        else {
            H_LOG( logger, Logger::SEVERE ) << "Unknown variable " << varName << std::endl;
            H_THROW( "Unknown variable name while parsing "+ getComponentName() + ": "
                    + varName );
        }
    } catch( h_exception& parseException ) {
        H_RETHROW( parseException, "Could not parse var: "+varName );
    }

    return;
}

//------------------------------------------------------------------------------
// documentation is inherited
void CarbonCycleSolver::prepareToRun() throw( h_exception )
{
    H_LOG( logger, Logger::DEBUG ) << "prepareToRun " << std::endl;

    cmodel = dynamic_cast<CarbonCycleModel*>( core->getComponentByCapability( D_ATMOSPHERIC_C ) );

    // initialize the solver's internal data
    t = core->getStartDate();
    nc = cmodel->ncpool();
    H_LOG( logger, Logger::DEBUG ) << "Carbon model in use is " << cmodel->getComponentName() << std::endl;
    H_LOG( logger, Logger::DEBUG ) << "Carbon model pools: " << nc << std::endl;
    H_ASSERT( nc > 0, "nc must be > 0" );
    // resize the array of carbon pool values
    c.resize(nc);

}

//------------------------------------------------------------------------------
// documentation is inherited
unitval CarbonCycleSolver::getData( const std::string& varName,
                                   const double date ) throw ( h_exception ) {

    unitval returnval;

    H_ASSERT( date == Core::undefinedIndex(), "Date not allowed for CarbonCycleSolver" );

    H_THROW( "Caller is requesting unknown variable: " + varName );

    return returnval;
}

void CarbonCycleSolver::reset(double time) throw(h_exception)
{
    // Only state maintained by this component is the time counter
    t = time;
    in_spinup = false;          // reset this in case we will be expected to rerun the spinup.
    H_LOG(logger, Logger::NOTICE)
        << getComponentName() << " reset to time= " << time << "\n";
}

//------------------------------------------------------------------------------
// documentation is inherited
void CarbonCycleSolver::shutDown()
{
	H_LOG( logger, Logger::DEBUG ) << "goodbye " << getComponentName() << std::endl;
    logger.close();
}

//------------------------------------------------------------------------------
/*! \brief              Dispatch function called by ODE solver
 *  \param[in] y        pools
 *  \param[in] dydt     pool changes
 *  \param[in] t        time
 *  \exception          If the carbon model returned failure flag we must throw
 *                      an exception to stop the ODE solver.
 */
void CarbonCycleSolver::ODEEvalFunctor::operator()( const std::vector<double>& y,
                                                    std::vector<double>& dydt,
                                                    double t ) throw ( bad_derivative_exception )
{
    // Note the std garuntees vetors are contigous so we can convert to array by
    // taking the address of the first value.
    int status = modelptr->calcderivs( t, &y[0], &dydt[0] );

    if( status != ODE_SUCCESS ) {
        bad_derivative_exception e(status);
        throw e;
    }
}

//------------------------------------------------------------------------------
/*! \brief              Observer callback called by ODE solver when a successful
 *                      step has been taken.
 *  \details            We use this callback to update our state variable time (t)
 *                      and in principle carbon pools (c) however the later is
 *                      already updated by the solver (pass by reference) so we will
 *                      skip copying here.
 *  \param[in] y        pools
 *  \param[in] t        time
 */
void CarbonCycleSolver::ODEEvalFunctor::operator()( const std::vector<double>& y, double t ) {
    // copy the current time to the original in CarbonCycleSolver
    (*this->t) = t;
}

//------------------------------------------------------------------------------
/*! \brief Support function for gsl_ode failure
 *  \param[in] stat     failure code
 *  \param[in] t0       start of time step
 *  \param[in] tmid     middle of time step
 *  \exception          will always happen; gsl solver has failed
 */
void CarbonCycleSolver::failure( int stat, double t0, double tmid ) throw( h_exception ) {
    H_LOG( logger, Logger::SEVERE ) << "gsl_ode_evolve_apply failed at t= " <<
    t0 << "  tinit= " << t << "  tmid = " << tmid << "  last dt= " <<
    dt << "\nError code: " << stat << "\ncvals:\n";
    for( int i=0; i<nc; ++i )
        H_LOG( logger,Logger::SEVERE ) << c[ i ] << "  ";
    H_LOG( logger,Logger::SEVERE ) << std::endl;
    H_THROW( "gsl_ode_evolve_apply failed." );
}

//------------------------------------------------------------------------------
// documentation is inherited
void CarbonCycleSolver::run( const double tnew ) throw ( h_exception )
{
    if(tnew <= t) {
        H_LOG(logger, Logger::SEVERE) << "run(): tnew= " << tnew << "   t= " << t << std::endl;
    }
    H_ASSERT( tnew > t, "solver tnew is not greater than t" );

    // Get the initial state data from the box model. c will be filled in
    // Note that we rely on the box model to handle the units.  Inside the
    // solver we strip the unit values and work with raw numbers.
    cmodel->getCValues( t, &c[0] );

    double t0   = t;  // stash this in case we need to report & diagnose an error
    // Now integrate from the beginning of the time step using the updated
    // slow params.  Note we can discard t0 and the values in cc
    cmodel->slowparameval( t, &c[0] );
    int retry = 0;

    H_LOG( logger, Logger::DEBUG ) << "Entering ODE solver " << t << "->" << tnew << std::endl;
    while( t < tnew && retry < MAX_CARBON_MODEL_RETRIES ) {

        H_LOG( logger, Logger::DEBUG ) << "Resetting evolver and stepper" << std::endl;
        double t_start = t;
        double t_target = tnew;

        while( t < t_target && retry < MAX_CARBON_MODEL_RETRIES ) {
            H_LOG( logger, Logger::NOTICE ) << "Attempting ODE solver " << t << "->" << t_target << " (" << t0 << "->" << tnew << ")" << std::endl;

            int stat = ODE_SUCCESS;
            ODEEvalFunctor odeFunctor( cmodel, &t );
            try {
                using namespace boost::numeric::odeint;
                typedef runge_kutta_dopri5<std::vector<double> > error_stepper_type;
                integrate_adaptive( make_controlled<error_stepper_type>( eps_abs, eps_rel ),
                         odeFunctor, c, t_start, t_target, dt, odeFunctor );
            } catch( bad_derivative_exception& e ) {
                stat = e.errorFlag;
            }

            if( stat == CARBON_CYCLE_RETRY ) {
                H_LOG( logger, Logger::NOTICE ) << "Carbon model requests retry #" << ++retry << " at t= " << t << std::endl;
                t_target = t_start + ( t_target - t_start ) / 2.0;
                t = t_start;

                dt = t_target - t;
                cmodel->getCValues( t, &c[0] );     // reset pools and inform model of new starting point
                H_LOG( logger, Logger::NOTICE ) << "New target is " << t_target << std::endl;
            } else if( stat != ODE_SUCCESS )
                failure( stat, t_start, t_target );
        }

        // We have exited GSL solver loop, but did we make it?
        if( retry < MAX_CARBON_MODEL_RETRIES ) {
            H_LOG( logger, Logger::NOTICE ) << "Success: we have reached " << t_target << std::endl;

            retry = 0;
            cmodel->stashCValues( t, &c[0] );   // update state
        } else {
            H_LOG( logger, Logger::SEVERE ) << "Failure after failure: t is " << t << "; we have not reached " << t_target << std::endl;

        }
    }
    H_ASSERT( t == tnew, "solver failure: t != tnew" );

    H_LOG( logger, Logger::NOTICE ) << "ODE solver success at t= " << t <<
    "  last dt= " << dt << std::endl;
    H_LOG( logger, Logger::DEBUG ) << "cvals\terrors\n";

    cmodel->record_state(tnew);

    H_LOG( logger, Logger::NOTICE ) << std::endl;
}

//------------------------------------------------------------------------------
/*! \brief      Run the carbon cycle to steady state, if possible
 *  \exception  epsilon must be >0
 *
 *  We normally want the carbon model to be in steady state, i.e. with unchanging
 *  carbon pools, at the end of the preindustrial period before the main run.
 *  To reach this point, run the carbon model until all dc/dt are ~0.
 */
bool CarbonCycleSolver::run_spinup( const int step ) throw( h_exception )
{

    if( !in_spinup ) {  // first time
        in_spinup = true;
        t = step-1;
        c_original.resize(nc);
        c_old.resize(nc);
        c_new.resize(nc);
        dcdt.resize(nc);
        // initialize to zero
        for(int i=0; i<nc; ++i)
          c_original[i] = c_old[i] = c_new[i] = dcdt[i] = 0.0;

        cmodel->getCValues( t, &c_original[0] );
        cmodel->record_state(t);
    }

    cmodel->getCValues( t, &c_old[0] );
    run( step );
    cmodel->getCValues( step, &c_new[0] );

    int max_dcdt_pool = 0;
    double max_dcdt = 0.0;

    for( int i=0; i<nc; i++ ) {     // find the biggest difference
        dcdt[ i ] = fabs( c_new[ i ] - c_old[ i ] );
        c_old[ i ] = c_new[ i ];
        if (dcdt[ i ] > max_dcdt) {
            max_dcdt = dcdt[i];
        }
    }

    bool spunup = ( max_dcdt < eps_spinup.value( U_PGC ) );

    if( spunup ) {
        Logger& glog = core->getGlobalLogger();
        H_LOG( glog, Logger::NOTICE ) << "Carbon model is spun up after " << step << " steps" << std::endl;
        H_LOG( logger, Logger::NOTICE ) << "Carbon model spun up after " << step << " steps. Max residual dc/dt="
        << max_dcdt << " (pool " << max_dcdt_pool << ")" << std::endl;
        for( int i=0; i<nc; i++ ) {
            H_LOG( logger, Logger::NOTICE ) << "New pool " << i << ":" << c_new[ i ]
            << " (delta=" << c_new[ i ]-c_original[ i ] << ")" << std::endl;
        }
        t = core->getStartDate();
        H_LOG( logger, Logger::NOTICE ) << "Resetting solver time counter to t= " << t << std::endl;
    }

    // Record the state as the state at the model start time.  This
    // will be repeatedly overwritten until the spinup is complete.
    cmodel->record_state(core->getStartDate());

    return spunup;
}

//------------------------------------------------------------------------------
/*! \brief visitor accept code
 */
void CarbonCycleSolver::accept( AVisitor* visitor )
{
    visitor->visit( this );
}

}


//==================================================================================================
/*!
  @file

  @copyright 2016 NumScale SAS
  @copyright 2016 J.T. Lapreste

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_FUNCTION_DIVIDES_INCLUDED
#define BOOST_SIMD_FUNCTION_DIVIDES_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{

 /*!
    @ingroup group-operator
    Function object implementing divides capabilities

    Perform the quotient of two parameters of the same type.

    @par Semantic

    For any value @c a and @c b of type @c T,

    @code
    T r = divides(a,b);
    @endcode

    returns the quotient of @c a by @c b

  **/
  const boost::dispatch::functor<tag::divides_> divides = {};
} }
#endif

#include <boost/simd/function/scalar/divides.hpp>
#include <boost/simd/function/scalar/divides.hpp>
#include <boost/simd/function/simd/divides.hpp>

#endif


#include <scitbx/array_family/boost_python/flex_fwd.h>

#include <scitbx/histogram.h>
#include <scitbx/boost_python/is_polymorphic_workaround.h>
#include <boost/python/class.hpp>
#include <boost/python/args.hpp>

SCITBX_BOOST_IS_POLYMORPHIC_WORKAROUND(
  scitbx::histogram<>)

namespace scitbx { namespace af { namespace boost_python { namespace {

  struct histogram_wrappers
  {
    typedef histogram<> w_t;
    static void
    wrap()
    {
      using namespace boost::python;
      class_<w_t>("histogram", no_init)
        .def(init<
          af::const_ref<double> const&,
          std::size_t>((
            arg("data"),
            arg("n_slots")=1000)))
        .def(init<
          af::const_ref<double> const&,
          double const&,
          double const&,
          std::size_t,
          double const&>((
            arg("data"),
            arg("data_min"),
            arg("data_max"),
            arg("n_slots")=1000,
            arg("relative_tolerance")=1e-4)))
        .def(init<
          w_t const&,
          af::const_ref<double> const&,
          double const&>((
            arg("other"),
            arg("data"),
            arg("relative_tolerance")=1e-4)))
        .enable_pickling()
        .def(init<
          double const&,
          double const&,
          double const&,
          af::shared<long> const&,
          std::size_t>()) // intentionally no arg
        .def("update",
          (void(w_t::*)(double const&, double const&)) &w_t::update, (
          arg("data_value"),
          arg("relative_tolerance")=1e-4))
        .def("update",
          (void(w_t::*)(w_t const&)) &w_t::update, (
          arg("other")))

        .def("update",
          (void(w_t::*)(af::shared<int> const&)) &w_t::update, (
          arg("from_ar")))

        .def("data_min", &w_t::data_min)
        .def("data_max", &w_t::data_max)
        .def("slot_width", &w_t::slot_width)
        .def("slot_centers", &w_t::slot_centers)
        .def("slots", &w_t::slots)
        .def("get_i_slot", &w_t::get_i_slot)
        .def("n_out_of_slot_range", &w_t::n_out_of_slot_range)
        .def("get_cutoff", &w_t::get_cutoff, (
          arg("max_points"),
          arg("relative_tolerance")=1e-4))
      ;
    }
  };

} // namespace <anonymous>

  void wrap_flex_histogram()
  {
    histogram_wrappers::wrap();
  }

}}} // namespace scitbx::af::boost_python


/*    Copyright (c) 2010-2019, Delft University of Technology
 *    All rigths reserved
 *
 *    This file is part of the Tudat. Redistribution and use in source and
 *    binary forms, with or without modification, are permitted exclusively
 *    under the terms of the Modified BSD license. You should have received
 *    a copy of the license with this file. If not, please or visit:
 *    http://tudat.tudelft.nl/LICENSE.
 */

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN

#include <limits>
#include <boost/test/unit_test.hpp>

#include "tudat/astro/basic_astro/physicalConstants.h"

#include "tudat/interface/sofa/earthOrientation.h"
#include "tudat/interface/sofa/sofaTimeConversions.h"
#include "tudat/basics/timeType.h"

namespace tudat
{
namespace unit_tests
{

using namespace tudat::sofa_interface;

BOOST_AUTO_TEST_SUITE( test_sofa_earth_orientation )

//! Test precession/nutation calculations from Sofa, using Sofa Earth orientation cookbook results.
BOOST_AUTO_TEST_CASE( testSofaPrecessionNutation )
{
    // Two-part Terrestrial time at which rotation is calculated.
    double testJulianDay1 = 2400000.5;
    double testJulianDay2 = 54195.500754444444444 * physical_constants::JULIAN_DAY;

    double arcSecondToRadian = 4.848136811095359935899141E-6;

    // Perform test for IAU_2000 and IAU_2006 cnventions
    for( unsigned int i = 0; i < 2; i++ )
    {
        // Get output data from SOFA cookbook.
        Eigen::Vector2d expectedPolePosition;
        double expectedCioLocator = 0.0;
        double dXTest = 0.0;
        double dYTest = 0.0;
        basic_astrodynamics::IAUConventions iauConventions;
        if( i == 0 )
        {
            iauConventions = basic_astrodynamics::iau_2000_a;

            expectedPolePosition.x( ) = 0.000712264729599;
            expectedPolePosition.y( ) = 0.000044385250426;
            expectedCioLocator = -0.002200475 * arcSecondToRadian;
            dXTest = 0.0001750 * arcSecondToRadian;
            dYTest = -0.000265 * arcSecondToRadian;
        }
        else
        {
            iauConventions = basic_astrodynamics::iau_2006;

            expectedPolePosition.x( ) = 0.000712264729525;
            expectedPolePosition.y( ) = 0.000044385248875;
            expectedCioLocator = -0.002200475 * arcSecondToRadian;
            dXTest = 0.0001750 * arcSecondToRadian;
            dYTest = -0.0002259 * arcSecondToRadian;

        }

        // Calculate X, Y pole position and CIO locator and compare against cookbook results.
        std::pair< Eigen::Vector2d, double > cipInGcrs =
                getPositionOfCipInGcrs( testJulianDay2, testJulianDay1, iauConventions );
        BOOST_CHECK_SMALL( expectedPolePosition.x( ) - ( cipInGcrs.first.x( ) + dXTest ), 5.0E-11 );
        BOOST_CHECK_SMALL( expectedPolePosition.y( ) - ( cipInGcrs.first.y( ) + dYTest ), 5.0E-11 );
        BOOST_CHECK_SMALL( expectedCioLocator - cipInGcrs.second, 2.0E-12 );

    }

}

//! Test GMST and ERA functions from Sofa.
BOOST_AUTO_TEST_CASE( testSofaEarthRotation )
{

    double testJulianDay1 = 2400000.5 + 54195;
    double testJulianDay2 = 0.500754444444444;

    double testUt1 = 2400000.5 + 54195;
    double testUt2 = 0.499999165813831;

    // Test correct copy/translation of Sofa fortran test from cookbook (GMST not directly given in cookbook).
    {
        double deltaPsi, deltaEpsilon;
        double meanObliquity;
        double rb[3][3], rp[3][3], rbp[3][3];
        double rn[3][3], rbpn[3][3];
        iauNut00a( testJulianDay1, testJulianDay2, &deltaPsi, &deltaEpsilon );
        iauPn00( testJulianDay1, testJulianDay2, deltaPsi, deltaEpsilon, &meanObliquity, rb, rp, rbp, rn, rbpn );
        // Transform dX,dY corrections from GCRS to mean of date.

        double arcSecondToRadian = 4.848136811095359935899141E-6;

        double dXTest = 0.17250 * arcSecondToRadian / 1000.0;
        double dYTest = -0.26500 * arcSecondToRadian / 1000.0;
        double angleVector1[3], angleVector2[3];
        angleVector1[0] = dXTest;
        angleVector1[1] = dYTest;
        angleVector1[2] = 0.0;
        iauRxp( rbpn, angleVector1, angleVector2 );
        double deltaPsiCorrection = angleVector2[0] / std::sin( meanObliquity );

        // Corrected nutation.;
        deltaPsi += deltaPsiCorrection;

        // Greenwich apparent sidereal time (IAU 1982/1994).
        double gst = ( iauGmst00( testUt1, testUt2, testJulianDay1, testJulianDay2 ) +
                       iauEe00( testJulianDay1, testJulianDay2, meanObliquity, deltaPsi ) )
                * 180.0 / mathematical_constants::PI;

        // Check if test data is correctly reprodcued
        double expectedAngle = 13.412417084674;
        BOOST_CHECK_SMALL( std::fabs( gst - expectedAngle ), 1.0E-12 );
    }

    // Compare direct against indirect GMST calculation.
    double expectedGmst = iauGmst00( testUt1, testUt2, testJulianDay1, testJulianDay2 );
    double calculatedGmst = calculateGreenwichMeanSiderealTime(
                testJulianDay2 * physical_constants::JULIAN_DAY, testUt2 * physical_constants::JULIAN_DAY,
                testJulianDay1, basic_astrodynamics::iau_2000_a );

    BOOST_CHECK_CLOSE_FRACTION( expectedGmst, calculatedGmst, std::numeric_limits< double >::epsilon( ) );

    // Calculate Earth rotation angle and comapre against result in cookbook.
    double earthRotationAngle = calculateEarthRotationAngle(
                testUt2 * physical_constants::JULIAN_DAY, testUt1 ) * 180.0 / mathematical_constants::PI;
    double expectedEarthRotationAngle = 13.318492966097;

    BOOST_CHECK_SMALL( std::fabs( expectedEarthRotationAngle - earthRotationAngle ), 1.0E-12 );

    earthRotationAngle = calculateEarthRotationAngleTemplated< double >(
                    ( testUt2 + testUt1 - basic_astrodynamics::JULIAN_DAY_ON_J2000 ) *
                physical_constants::JULIAN_DAY ) * 180.0 / mathematical_constants::PI;
    BOOST_CHECK_SMALL( std::fabs( expectedEarthRotationAngle - earthRotationAngle ), 1.0E-7 );

    earthRotationAngle = calculateEarthRotationAngleTemplated< Time >(
                    tudat::Time( ( testUt1 - basic_astrodynamics::JULIAN_DAY_ON_J2000 ) * 24,
                                 static_cast< long double >( testUt2 ) * physical_constants::JULIAN_DAY_LONG ) ) *
            180.0 / mathematical_constants::PI;
    BOOST_CHECK_SMALL( std::fabs( expectedEarthRotationAngle - earthRotationAngle ), 1.0E-12 );

    // Test ERA for negative time since J2000
    testUt1 = basic_astrodynamics::JULIAN_DAY_ON_J2000 - 200.0;
    testUt2 = 0.499999165813831;
    double directEarthRotationAngle = calculateEarthRotationAngle(
                testUt2 * physical_constants::JULIAN_DAY, testUt1 ) * 180.0 / mathematical_constants::PI;

    earthRotationAngle = calculateEarthRotationAngleTemplated< double >(
                    ( testUt2 + testUt1 - basic_astrodynamics::JULIAN_DAY_ON_J2000 ) *
                physical_constants::JULIAN_DAY ) * 180.0 / mathematical_constants::PI;
    BOOST_CHECK_SMALL( std::fabs( directEarthRotationAngle - earthRotationAngle ), 1.0E-7 );

    earthRotationAngle = calculateEarthRotationAngleTemplated< Time >(
                    tudat::Time( ( testUt1 - basic_astrodynamics::JULIAN_DAY_ON_J2000 ) * 24,
                                 static_cast< long double >( testUt2 ) * physical_constants::JULIAN_DAY_LONG ) ) *
            180.0 / mathematical_constants::PI;
    BOOST_CHECK_SMALL( std::fabs( directEarthRotationAngle - earthRotationAngle ), 1.0E-12 );
}

BOOST_AUTO_TEST_SUITE_END( )

} // namespace unit_tests

} // namespace tudat