// -----------------------------------------------------------------------------
/**
 *  @brief Problem using a PCA of images
 *  @author Jose M. Buenaposada
 *  @date 2012/08/11
 *  @version $revision$
 *
 *  $id$
 *
 *  Grupo de investigación en Percepción Computacional y Robótica)
 *  (Perception for Computers & Robots research Group)
 *  Facultad de Informática (Computer Science School)
 *  Universidad Politécnica de Madrid (UPM) (Madrid Technical University)
 *  http://www.dia.fi.upm.es/~pcr
 *
 */
// -----------------------------------------------------------------------------

// ------------------ RECURSION PROTECTION -------------------------------------
#ifndef IMAGE_PCA_FACTORIZED_PROBLEM_HPP
#define IMAGE_PCA_FACTORIZED_PROBLEM_HPP

// ----------------------- INCLUDES --------------------------------------------
#include "factorized_jacobian_problem.hpp"
#include "image_pca_model.hpp"
#include <boost/shared_ptr.hpp>
#include <vector>
#include <opencv/cv.h>

namespace upm { namespace pcr
{
// ----------------------------------------------------------------------------
/**
 *  Definition of an smart pointer to the ImagePCAFactorizedProblem type
 */
// -----------------------------------------------------------------------------
class ImagePCAFactorizedProblem;
typedef boost::shared_ptr<ImagePCAFactorizedProblem> ImagePCAFactorizedProblemPtr;

// -----------------------------------------------------------------------------
/**
 *  Definition of a container of smart pointers to ImagePCAFactorizedProblem class
 */
// -----------------------------------------------------------------------------
typedef std::vector<ImagePCAFactorizedProblem> ImagePCAFactorizedProblems;

// ----------------------------------------------------------------------------
/**
 * @class ImagePCAFactorizedProblem
 * @brief The interface for the ImagePCAFactorizedProblem to be used with Optimizer
 *
 * The factorization methods assume that the optimization problem jacobian J,
 * can be factorized into J=M0*Sigma matrix where Sigma matrix depends only
 * on the motion parameters and M0 matrix depends only on the object model
 * features and coordinates.
 */
// -----------------------------------------------------------------------------
class ImagePCAFactorizedProblem: public FactorizedJacobianProblem
{
public:

  ImagePCAFactorizedProblem
    (
    ImagePCAModelPtr object_model,
    MotionModelPtr motion_model
    );

  virtual ~ImagePCAFactorizedProblem
    ();

  virtual double
  computeCostFunction
    (
    cv::Mat& residual_vector,
    cv::Mat& params,
    cv::Mat& delta_params,
    cv::Mat& image
    );

  virtual cv::Mat
  computeResidual
    (
    cv::Mat& image,
    cv::Mat& params
    );

  virtual cv::Mat
  updateMotionParams
    (
    cv::Mat& params,
    cv::Mat& inc_params
    );

  cv::Mat
  computeInverseJacobian
    (
    cv::Mat& params
    );

protected:

  virtual cv::Mat
  computeSigmaMatrix
    (
    cv::Mat& params
    ) = 0;

  virtual cv::Mat
  computeM0Matrix
    () = 0;

  virtual void
  initialize
    ();

 ImagePCAModelPtr m_pca_model;

  cv::Mat m_M0t_NB;
  cv::Mat m_M0t_NB_M0;

  /** This is the last rectified (warped image) seen as a vector */
  cv::Mat m_features_vector;
};

}; }; // namespace

#endif


// The contents of this file are in the public domain. See LICENSE_FOR_EXAMPLE_PROGRAMS.txt
/*
    This is an example illustrating the use of the deep learning tools from the dlib C++
    Library.  I'm assuming you have already read the dnn_introduction_ex.cpp, the
    dnn_introduction2_ex.cpp and the dnn_introduction3_ex.cpp examples.  In this example
    program we are going to show how one can train a neural network using an unsupervised
    loss function.  In particular, we will train the ResNet50 model from the paper
    "Deep Residual Learning for Image Recognition" by Kaiming He, Xiangyu Zhang, Shaoqing
    Ren, Jian Sun.

    To train the unsupervised loss, we will use the self-supervised learning (SSL) method
    called Barlow Twins, introduced in this paper:
    "Barlow Twins: Self-Supervised Learning via Redundancy Reduction" by Jure Zbontar,
    Li Jing, Ishan Misra, Yann LeCun, Stéphane Deny.

    The paper contains a good explanation on how and why this works, but the main idea
    behind the Barlow Twins method is:
        - generate two distorted views of a batch of images: YA, YB
        - feed them to a deep neural network and obtain their representations and
          and batch normalize them: ZA, ZB
        - compute the empirical cross-correlation matrix between both feature
          representations as: C = trans(ZA) * ZB.
        - make C as close as possible to the identity matrix.

    This removes the redundancy of the feature representations, by maximizing the
    encoded information about the images themselves, while minimizing the information
    about the transforms and data augmentations used to obtain the representations.

    The original Barlow Twins paper uses the ImageNet dataset, but in this example we
    are using CIFAR-10, so we will follow the recommendations of this paper, instead:
    "A Note on Connecting Barlow Twins with Negative-Sample-Free Contrastive Learning"
    by Yao-Hung Hubert Tsai, Shaojie Bai, Louis-Philippe Morency, Ruslan Salakhutdinov,
    in which they experiment with Barlow Twins on CIFAR-10 and Tiny ImageNet.  Since
    the CIFAR-10 contains relatively small images, we will define a ResNet50 architecture
    that doesn't downsample the input in the first convolutional layer, and doesn't have
    a max pooling layer afterwards, like the paper does.
*/

#include <dlib/dnn.h>
#include <dlib/data_io.h>
#include <dlib/cmd_line_parser.h>
#include <dlib/gui_widgets.h>

using namespace std;
using namespace dlib;

// A custom definition of ResNet50 with a downsampling factor of 8 instead of 32.
// It is essentially the original ResNet50, but without the max pooling and a
// convolutional layer with a stride of 1 instead of 2 at the input.
namespace resnet50
{
    using namespace dlib;
    template <template <typename> class BN>
    struct def
    {
        template <long N, int K, int S, typename SUBNET>
        using conv = add_layer<con_<N, K, K, S, S, K / 2, K / 2>, SUBNET>;

        template<long N, int S, typename SUBNET>
        using bottleneck = BN<conv<4 * N, 1, 1, relu<BN<conv<N, 3, S, relu<BN<conv<N, 1, 1, SUBNET>>>>>>>>;

        template <long N,  typename SUBNET>
        using residual = add_prev1<bottleneck<N, 1, tag1<SUBNET>>>;

        template <typename SUBNET> using res_512 = relu<residual<512, SUBNET>>;
        template <typename SUBNET> using res_256 = relu<residual<256, SUBNET>>;
        template <typename SUBNET> using res_128 = relu<residual<128, SUBNET>>;
        template <typename SUBNET> using res_64  = relu<residual<64, SUBNET>>;

        template <long N, int S, typename SUBNET>
        using transition = add_prev2<BN<conv<4 * N, 1, S, skip1<tag2<bottleneck<N, S, tag1<SUBNET>>>>>>>;

        template <typename INPUT>
        using backbone = avg_pool_everything<
            repeat<2, res_512, transition<512, 2,
            repeat<5, res_256, transition<256, 2,
            repeat<3, res_128, transition<128, 2,
            repeat<2, res_64,  transition<64, 1,
            relu<BN<conv<64, 3, 1,INPUT>>>>>>>>>>>>;
    };
};

// This model namespace contains the definitions for:
// - SSL model using the Barlow Twins loss, a projector head and an input_rgb_image_pair.
// - Classifier model using the loss_multiclass_log, a fc layer and an input_rgb_image.
namespace model
{
    template <typename SUBNET> using projector = fc<128, relu<bn_fc<fc<512, SUBNET>>>>;
    template <typename SUBNET> using classifier = fc<10, SUBNET>;

    using train = loss_barlow_twins<projector<resnet50::def<bn_con>::backbone<input_rgb_image_pair>>>;
    using infer = loss_multiclass_log<classifier<resnet50::def<affine>::backbone<input_rgb_image>>>;
}

rectangle make_random_cropping_rect(
    const matrix<rgb_pixel>& image,
    dlib::rand& rnd
)
{
    const double mins = 7. / 15.;
    const double maxs = 7. / 8.;
    const auto scale = rnd.get_double_in_range(mins, maxs);
    const auto size = scale * std::min(image.nr(), image.nc());
    const rectangle rect(size, size);
    const point offset(rnd.get_random_32bit_number() % (image.nc() - rect.width()),
                       rnd.get_random_32bit_number() % (image.nr() - rect.height()));
    return move_rect(rect, offset);
}

matrix<rgb_pixel> augment(
    const matrix<rgb_pixel>& image,
    const bool prime,
    dlib::rand& rnd
)
{
    matrix<rgb_pixel> crop;
    // blur
    matrix<rgb_pixel> blurred;
    const double sigma = rnd.get_double_in_range(0.1, 1.1);
    if (!prime || (prime && rnd.get_random_double() < 0.1))
    {
        const auto rect = gaussian_blur(image, blurred, sigma);
        extract_image_chip(blurred, rect, crop);
        blurred = crop;
    }
    else
    {
        blurred = image;
    }

    // randomly crop
    const auto rect = make_random_cropping_rect(image, rnd);
    extract_image_chip(blurred, chip_details(rect, chip_dims(32, 32)), crop);

    // image left-right flip
    if (rnd.get_random_double() < 0.5)
        flip_image_left_right(crop);

    // color augmentation
    if (rnd.get_random_double() < 0.8)
        disturb_colors(crop, rnd, 0.5, 0.5);

    // grayscale
    if (rnd.get_random_double() < 0.2)
    {
        matrix<unsigned char> gray;
        assign_image(gray, crop);
        assign_image(crop, gray);
    }

    // solarize
    if (prime && rnd.get_random_double() < 0.2)
    {
        for (auto& p : crop)
        {
            if (p.red > 128)
                p.red = 255 - p.red;
            if (p.green > 128)
                p.green = 255 - p.green;
            if (p.blue > 128)
                p.blue = 255 - p.blue;
        }
    }
    return crop;
}

int main(const int argc, const char** argv)
try
{
    // The default settings are fine for the example already.
    command_line_parser parser;
    parser.add_option("batch", "set the mini batch size per GPU (default: 64)", 1);
    parser.add_option("dims", "set the projector dimensions (default: 128)", 1);
    parser.add_option("lambda", "penalize off-diagonal terms (default: 1/dims)", 1);
    parser.add_option("learning-rate", "set the initial learning rate (default: 1e-3)", 1);
    parser.add_option("min-learning-rate", "set the min learning rate (default: 1e-5)", 1);
    parser.add_option("num-gpus", "number of GPUs (default: 1)", 1);
    parser.set_group_name("Help Options");
    parser.add_option("h", "alias for --help");
    parser.add_option("help", "display this message and exit");
    parser.parse(argc, argv);

    if (parser.number_of_arguments() < 1 || parser.option("h") || parser.option("help"))
    {
        cout << "This example needs the CIFAR-10 dataset to run." << endl;
        cout << "You can get CIFAR-10 from https://www.cs.toronto.edu/~kriz/cifar.html" << endl;
        cout << "Download the binary version the dataset, decompress it, and put the 6" << endl;
        cout << "bin files in a folder.  Then give that folder as input to this program." << endl;
        parser.print_options();
        return EXIT_SUCCESS;
    }

    const size_t num_gpus = get_option(parser, "num-gpus", 1);
    const size_t batch_size = get_option(parser, "batch", 64) * num_gpus;
    const long dims = get_option(parser, "dims", 128);
    const double lambda = get_option(parser, "lambda", 1.0 / dims);
    const double learning_rate = get_option(parser, "learning-rate", 1e-3);
    const double min_learning_rate = get_option(parser, "min-learning-rate", 1e-5);

    // Load the CIFAR-10 dataset into memory.
    std::vector<matrix<rgb_pixel>> training_images, testing_images;
    std::vector<unsigned long> training_labels, testing_labels;
    load_cifar_10_dataset(parser[0], training_images, training_labels, testing_images, testing_labels);

    // Initialize the model with the specified projector dimensions and lambda.  According to the
    // second paper, lambda = 1/dims works well on CIFAR-10.
    model::train net((loss_barlow_twins_(lambda)));
    layer<1>(net).layer_details().set_num_outputs(dims);
    disable_duplicative_biases(net);
    dlib::rand rnd;
    std::vector<int> gpus(num_gpus);
    std::iota(gpus.begin(), gpus.end(), 0);

    // Train the feature extractor using the Barlow Twins method
    {
        dnn_trainer<model::train, adam> trainer(net, adam(1e-6, 0.9, 0.999), gpus);
        trainer.set_mini_batch_size(batch_size);
        trainer.set_learning_rate(learning_rate);
        trainer.set_min_learning_rate(min_learning_rate);
        trainer.set_iterations_without_progress_threshold(10000);
        trainer.set_synchronization_file("barlow_twins_sync");
        trainer.be_verbose();
        cout << trainer << endl;

        // During the training, we will compute the empirical cross-correlation matrix
        // between the features of both versions of the augmented images.  This matrix
        // should be getting close to the identity matrix as the training progresses.
        // Note that this step is already done in the loss layer, and it's not necessary
        // to do it here for the example to work.  However, it provides a nice
        // visualization of the training progress: the closer to the identity matrix,
        // the better.
        resizable_tensor eccm;
        eccm.set_size(dims, dims);
        // Some tensors needed to perform batch normalization
        resizable_tensor za_norm, zb_norm, means, invstds, rms, rvs, gamma, beta;
        const double eps = DEFAULT_BATCH_NORM_EPS;
        gamma.set_size(1, dims);
        beta.set_size(1, dims);
        image_window win;

        std::vector<std::pair<matrix<rgb_pixel>, matrix<rgb_pixel>>> batch;
        while (trainer.get_learning_rate() >= trainer.get_min_learning_rate())
        {
            batch.clear();
            while (batch.size() < trainer.get_mini_batch_size())
            {
                const auto idx = rnd.get_random_32bit_number() % training_images.size();
                auto image = training_images[idx];
                batch.emplace_back(augment(image, false, rnd), augment(image, true, rnd));
            }
            trainer.train_one_step(batch);

            // Compute the empirical cross-correlation matrix every 100 steps. Again,
            // this is not needed for the training to work, but it's nice to visualize.
            if (trainer.get_train_one_step_calls() % 100 == 0)
            {
                // Wait for threaded processing to stop in the trainer.
                trainer.get_net(force_flush_to_disk::no);
                // Get the output from the last fc layer
                const auto& out = net.subnet().get_output();
                // The trainer might have synchronized its state to the disk and cleaned
                // the network state. If that happens, the output will be empty, in
                // which case, we just skip the empirical cross-correlation matrix
                // computation.
                if (out.size() == 0)
                    continue;
                // Separate both augmented versions of the images
                alias_tensor split(out.num_samples() / 2, dims);
                auto za = split(out);
                auto zb = split(out, split.size());
                gamma = 1;
                beta = 0;
                // Perform batch normalization on each feature representation, independently.
                tt::batch_normalize(eps, za_norm, means, invstds, 1, rms, rvs, za, gamma, beta);
                tt::batch_normalize(eps, zb_norm, means, invstds, 1, rms, rvs, za, gamma, beta);
                // Compute the empirical cross-correlation matrix between the features and
                // visualize it.
                tt::gemm(0, eccm, 1, za_norm, true, zb_norm, false);
                eccm /= batch_size;
                win.set_image(abs(mat(eccm)) * 255);
                win.set_title("Barlow Twins step#: " + to_string(trainer.get_train_one_step_calls()));
            }
        }
        trainer.get_net();
        net.clean();
        // After training, we can discard the projector head and just keep the backone
        // to train it or finetune it on other downstream tasks.
        serialize("resnet50_self_supervised_cifar_10.net") << layer<5>(net);
    }

    // To check the quality of the learned feature representations, we will train a linear
    // classififer on top of the frozen backbone.
    model::infer inet;
    // Assign the network, without the projector, which is only used for the self-supervised
    // training.
    layer<2>(inet) = layer<5>(net);
    // Freeze the backbone
    set_all_learning_rate_multipliers(layer<2>(inet), 0);
    // Train the network
    {
        dnn_trainer<model::infer, adam> trainer(inet, adam(1e-6, 0.9, 0.999), gpus);
        // Since this model doesn't train with pairs, just single images, we can increase
        // the batch size.
        trainer.set_mini_batch_size(2 * batch_size);
        trainer.set_learning_rate(learning_rate);
        trainer.set_min_learning_rate(min_learning_rate);
        trainer.set_iterations_without_progress_threshold(5000);
        trainer.set_synchronization_file("cifar_10_sync");
        trainer.be_verbose();
        cout << trainer << endl;

        std::vector<matrix<rgb_pixel>> images;
        std::vector<unsigned long> labels;
        while (trainer.get_learning_rate() >= trainer.get_min_learning_rate())
        {
            images.clear();
            labels.clear();
            while (images.size() < trainer.get_mini_batch_size())
            {
                const auto idx = rnd.get_random_32bit_number() % training_images.size();
                images.push_back(augment(training_images[idx], false, rnd));
                labels.push_back(training_labels[idx]);
            }
            trainer.train_one_step(images, labels);
        }
        trainer.get_net();
        inet.clean();
        serialize("resnet50_cifar_10.dnn") << inet;
    }

    // Finally, we can compute the accuracy of the model on the CIFAR-10 train and test images.
    auto compute_accuracy = [&inet, batch_size](
        const std::vector<matrix<rgb_pixel>>& images,
        const std::vector<unsigned long>& labels
    )
    {
        size_t num_right = 0;
        size_t num_wrong = 0;
        const auto preds = inet(images, batch_size * 2);
        for (size_t i = 0; i < labels.size(); ++i)
        {
            if (labels[i] == preds[i])
                ++num_right;
            else
                ++num_wrong;
        }
        cout << "num right:  " << num_right << endl;
        cout << "num wrong:  " << num_wrong << endl;
        cout << "accuracy:   " << num_right / static_cast<double>(num_right + num_wrong) << endl;
        cout << "error rate: " << num_wrong / static_cast<double>(num_right + num_wrong) << endl;
    };

    // If everything works as expected, we should get accuracies that are between 87% and 90%.
    cout << "training accuracy" << endl;
    compute_accuracy(training_images, training_labels);
    cout << "\ntesting accuracy" << endl;
    compute_accuracy(testing_images, testing_labels);
    return EXIT_SUCCESS;
}
catch (const exception& e)
{
    cout << e.what() << endl;
    return EXIT_FAILURE;
}


//           Copyright Matthew Pulver 2018 - 2019.
// 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)

#include "test_autodiff.hpp"
#include <boost/math/special_functions.hpp>

BOOST_AUTO_TEST_SUITE(test_autodiff_6)

/*********************************************************************************************************************
 * special functions tests
 *********************************************************************************************************************/

BOOST_AUTO_TEST_CASE_TEMPLATE(ellint_1_hpp, T, all_float_types) {
  using test_constants = test_constants_t<T>;
  static constexpr auto m = test_constants::order;
  test_detail::RandomSample<T> k_sampler{T{-1}, T{1}};
  test_detail::RandomSample<T> phi_sampler{-boost::math::constants::two_pi<T>(),
                                           boost::math::constants::two_pi<T>()};
  for (auto i : boost::irange(test_constants::n_samples)) {
    std::ignore = i;
    auto k = k_sampler.next();
    auto phi = phi_sampler.next();
    BOOST_CHECK_CLOSE(boost::math::ellint_1(make_fvar<T, m>(k)).derivative(0u),
                      boost::math::ellint_1(k),
                      2.5e3 * test_constants::pct_epsilon());
    BOOST_CHECK_CLOSE(
        boost::math::ellint_1(make_fvar<T, m>(k), make_fvar<T, m>(phi))
            .derivative(0u),
        boost::math::ellint_1(k, phi), 1e4 * test_constants::pct_epsilon());
  }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(ellint_2_hpp, T, all_float_types) {
  using test_constants = test_constants_t<T>;
  static constexpr auto m = test_constants::order;
  test_detail::RandomSample<T> k_sampler{-1, 1};
  test_detail::RandomSample<T> phi_sampler{-boost::math::constants::two_pi<T>(),
                                           boost::math::constants::two_pi<T>()};
  for (auto i : boost::irange(test_constants::n_samples)) {
    std::ignore = i;
    auto k = k_sampler.next();
    auto phi = phi_sampler.next();
    BOOST_CHECK_CLOSE(boost::math::ellint_2(make_fvar<T, m>(k)).derivative(0u),
                      boost::math::ellint_2(k),
                      2.5e3 * test_constants::pct_epsilon());
    BOOST_CHECK_CLOSE(
        boost::math::ellint_2(make_fvar<T, m>(k), make_fvar<T, m>(phi))
            .derivative(0u),
        boost::math::ellint_2(k, phi), 2.5e3 * test_constants::pct_epsilon());
  }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(ellint_3_hpp, T, all_float_types) {
  using boost::math::nextafter;
  using boost::multiprecision::nextafter;

  using boost::math::differentiation::detail::sin;
  using boost::multiprecision::min;
  using std::min;
  using std::sin;

  using test_constants = test_constants_t<T>;
  static constexpr auto m = test_constants::order;
  test_detail::RandomSample<T> k_sampler{-1, 1};
  test_detail::RandomSample<T> n_sampler{-2000, 2000};
  test_detail::RandomSample<T> phi_sampler{-boost::math::constants::two_pi<T>(),
                                           boost::math::constants::two_pi<T>()};
  for (auto i : boost::irange(test_constants::n_samples)) {
    std::ignore = i;
    auto k = k_sampler.next();
    auto phi = phi_sampler.next();
    auto n = (min)((min)(n_sampler.next(), T(1) / (sin(phi) * sin(phi))),
                   nextafter(T(1), T(0)));
    BOOST_CHECK_CLOSE(boost::math::ellint_3(make_fvar<T, m>(k),
                                            make_fvar<T, m>(n),
                                            make_fvar<T, m>(phi))
                          .derivative(0u),
                      boost::math::ellint_3(k, n, phi),
                      2.5e3 * test_constants::pct_epsilon());
  }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(ellint_d_hpp, T, all_float_types) {
  using test_constants = test_constants_t<T>;
  static constexpr auto m = test_constants::order;
  test_detail::RandomSample<T> k_sampler{-1, 1};
  test_detail::RandomSample<T> phi_sampler{-boost::math::constants::two_pi<T>(),
                                           boost::math::constants::two_pi<T>()};
  for (auto i : boost::irange(test_constants::n_samples)) {
    std::ignore = i;
    auto k = k_sampler.next();
    auto phi = phi_sampler.next();
    BOOST_CHECK_CLOSE(boost::math::ellint_d(make_fvar<T, m>(k)).derivative(0u),
                      boost::math::ellint_d(k),
                      2.5e3 * test_constants::pct_epsilon());
    BOOST_CHECK_CLOSE(
        boost::math::ellint_d(make_fvar<T, m>(k), make_fvar<T, m>(phi))
            .derivative(0u),
        boost::math::ellint_d(k, phi), 2.5e3 * test_constants::pct_epsilon());
  }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(ellint_rf_hpp, T, all_float_types) {

  using boost::math::tools::max;
  using std::max;

  using boost::math::nextafter;
  using std::nextafter;

  using test_constants = test_constants_t<T>;
  static constexpr auto m = test_constants::order;

  test_detail::RandomSample<T> x_sampler{0, 2000};
  test_detail::RandomSample<T> y_sampler{0, 2000};
  test_detail::RandomSample<T> z_sampler{0, 2000};
  for (auto i : boost::irange(test_constants::n_samples)) {
    std::ignore = i;
    auto x = nextafter(x_sampler.next(), ((std::numeric_limits<T>::max))());
    auto y = nextafter(y_sampler.next(), ((std::numeric_limits<T>::max))());
    auto z = nextafter(z_sampler.next(), ((std::numeric_limits<T>::max))());

    BOOST_CHECK_CLOSE(
        boost::math::ellint_rf(make_fvar<T, m>(x), make_fvar<T, m>(y),
                               make_fvar<T, m>(z))
            .derivative(0u),
        boost::math::ellint_rf(x, y, z), 2.5e3 * test_constants::pct_epsilon());
  }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(ellint_rc_hpp, T, all_float_types) {
  using boost::math::fpclassify;
  using boost::math::nextafter;
  using boost::math::signbit;
  using boost::math::tools::max;
  using boost::multiprecision::fpclassify;
  using boost::multiprecision::signbit;
  using std::max;
  using std::nextafter;

  using test_constants = test_constants_t<T>;
  static constexpr auto m = test_constants::order;
  test_detail::RandomSample<T> x_sampler{0, 2000};
  test_detail::RandomSample<T> y_sampler{0, 2000};
  for (auto i : boost::irange(test_constants::n_samples)) {
    std::ignore = i;
    auto x = x_sampler.next();
    auto y = T(0);
    while (fpclassify(T(y)) == FP_ZERO) {
      y = (max)(y_sampler.next(),
                nextafter(T(0), T(signbit(y) ? -1 : 1) *
                                    ((std::numeric_limits<T>::max))()));
    }

    BOOST_CHECK_CLOSE(
        boost::math::ellint_rc(make_fvar<T, m>(x), make_fvar<T, m>(y))
            .derivative(0u),
        boost::math::ellint_rc(x, y), 2.5e3 * test_constants::pct_epsilon());
  }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(ellint_rj_hpp, T, all_float_types) {
  using boost::math::fpclassify;
  using boost::math::nextafter;
  using boost::math::signbit;
  using boost::math::tools::max;
  using boost::multiprecision::fpclassify;
  using boost::multiprecision::signbit;

  using std::max;
  using std::nextafter;

  using test_constants = test_constants_t<T>;
  static constexpr auto m = test_constants::order;

  test_detail::RandomSample<T> x_sampler{0, 2000};
  test_detail::RandomSample<T> y_sampler{0, 2000};
  test_detail::RandomSample<T> z_sampler{0, 2000};
  test_detail::RandomSample<T> p_sampler{-2000, 2000};

  for (auto i : boost::irange(test_constants::n_samples)) {
    std::ignore = i;
    auto x = x_sampler.next();
    auto y = (x != 0 ? 1 : 0) + y_sampler.next();
    auto z = ((x == 0 || y == 0) ? 1 : 0) + z_sampler.next();
    auto p = T(0);

    while (fpclassify(T(p)) == FP_ZERO) {
      p = (max)(p_sampler.next(),
                nextafter(T(0), T(signbit(p) ? -1 : 1) *
                                    ((std::numeric_limits<T>::max))()));
    }
    BOOST_CHECK_CLOSE(
        boost::math::ellint_rj(make_fvar<T, m>(x), make_fvar<T, m>(y),
                               make_fvar<T, m>(z), make_fvar<T, m>(p))
            .derivative(0u),
        boost::math::ellint_rj(x, y, z, p),
        2.5e3 * test_constants::pct_epsilon());
  }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(ellint_rd_hpp, T, all_float_types) {
  using test_constants = test_constants_t<T>;
  static constexpr auto m = test_constants::order;
  test_detail::RandomSample<T> x_sampler{0, 2000};
  test_detail::RandomSample<T> y_sampler{0, 2000};
  test_detail::RandomSample<T> z_sampler{0, 2000};
  for (auto i : boost::irange(test_constants::n_samples)) {
    std::ignore = i;
    auto x = x_sampler.next();
    auto y = (x == 0 ? 1 : 0) + y_sampler.next();
    auto z = z_sampler.next();
    BOOST_CHECK_CLOSE(
        boost::math::ellint_rd(make_fvar<T, m>(x), make_fvar<T, m>(y),
                               make_fvar<T, m>(z))
            .derivative(0u),
        boost::math::ellint_rd(x, y, z), 2.5e3 * test_constants::pct_epsilon());
  }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(ellint_rg_hpp, T, all_float_types) {

  using boost::math::nextafter;
  using std::nextafter;

  using test_constants = test_constants_t<T>;
  static constexpr auto m = test_constants::order;
  test_detail::RandomSample<T> x_sampler{0, 2000};
  test_detail::RandomSample<T> y_sampler{0, 2000};
  test_detail::RandomSample<T> z_sampler{0, 2000};

  for (auto i : boost::irange(test_constants::n_samples)) {
    std::ignore = i;
    auto x = nextafter(x_sampler.next(), ((std::numeric_limits<T>::max))());
    auto y = nextafter(y_sampler.next(), ((std::numeric_limits<T>::max))());
    auto z = z_sampler.next();
    BOOST_CHECK_CLOSE(
        boost::math::ellint_rg(make_fvar<T, m>(x), make_fvar<T, m>(y),
                               make_fvar<T, m>(z))
            .derivative(0u),
        boost::math::ellint_rg(x, y, z), 50 * test_constants::pct_epsilon());
  }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(erf_hpp, T, all_float_types) {
  using test_constants = test_constants_t<T>;
  static constexpr auto m = test_constants::order;
  test_detail::RandomSample<T> x_sampler{-2000, 2000};
  for (auto i : boost::irange(test_constants::n_samples)) {
    std::ignore = i;
    auto x = x_sampler.next();

    BOOST_CHECK(isNearZero(erf(make_fvar<T, m>(x)).derivative(0u) -
                           boost::math::erf(x)));
    BOOST_CHECK(isNearZero(erfc(make_fvar<T, m>(x)).derivative(0u) -
                           boost::math::erfc(x)));
  }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(expint_hpp, T, all_float_types) {
  using test_constants = test_constants_t<T>;
  static constexpr auto m = test_constants::order;
  test_detail::RandomSample<T> x_sampler{1, 83};
  for (auto n :
       boost::irange(1u, static_cast<unsigned>(test_constants::n_samples))) {
    auto x = x_sampler.next();
    BOOST_CHECK_CLOSE(boost::math::expint(n, make_fvar<T, m>(x)).derivative(0u),
                      boost::math::expint(n, x),
                      200 * test_constants::pct_epsilon());

    for (auto y : {-1, 1}) {
      BOOST_CHECK_CLOSE(
          boost::math::expint(make_fvar<T, m>(x * y)).derivative(0u),
          boost::math::expint(x * y), 200 * test_constants::pct_epsilon());
    }
  }
}

BOOST_AUTO_TEST_SUITE_END()


// Deal.ii
#include <deal.II/base/tensor_function.h>
#include <deal.II/grid/tria.h>
#include <deal.II/grid/grid_generator.h>
#include <deal.II/grid/grid_out.h>

// STL
#include <iostream>
#include <fstream>
#include <cmath>

//#include "reconstruction_mesh.hpp"

#include <deal.II/base/logstream.h>

using namespace dealii;

template <int dim> class AdvectionField : public TensorFunction<1, dim> {
public:
  AdvectionField() : TensorFunction<1, dim>() {}

  virtual Tensor<1, dim> value(const Point<dim> &point) const override;
  virtual void value_list(const std::vector<Point<dim>> &points,
                          std::vector<Tensor<1, dim>> &values) const override;

private:
  const double pi = numbers::PI;
};

template <int dim>
Tensor<1, dim> AdvectionField<dim>::value(const Point<dim> &p) const
{
  const double t = this->get_time();

  Tensor<1, dim> value;
  value.clear();

  double t0 = 0;

    const double tn = 1;

    const double n=10;

    const double dt = (tn-t0)/n;

    const double c=3;

  // Here velocity is consider to be constant let it be 1 m/s.

  for (unsigned int d = 0; d < dim; ++d)

    {

      value[d] =0.8 * std::sin( 3.14* p[1]);

     // value[d] =1; //0.8 * std::sin( numbers::PI * p[1]);

    }

  return value;
}

template <int dim>
void AdvectionField<dim>::value_list(
    const std::vector<Point<dim>> &points,
    std::vector<Tensor<1, dim>> &values) const {
  Assert(points.size() == values.size(),
         ExcDimensionMismatch(points.size(), values.size()));

  for (unsigned int p = 0; p < points.size(); ++p) {
    values[p].clear();
    values[p] = value(points[p]);
  }
}

///////////////////////////////////////////////////////////////

template <int dim> class RK4 : public Tensor<1, dim> {
public:
  RK4() : Tensor<1, dim>() {}
  virtual Tensor<1, dim> value(const Point<dim> &p) const;

private:
  AdvectionField<dim> advection_field;
};

template <int dim> Tensor<1, dim> RK4<dim>::value(const Point<dim> &p) const {

  Tensor<1, dim> yn;

  double t0 = 0;

  const double tn = 1;

  const double n=10;

  const double dt = (tn-t0)/n;

  /* Here the equation is dy/dt =c; where c is velocity y is the vertex of mesh and t is the time
   *
   */

  yn = advection_field.value(p);

  for (double t0 = 0; t0 <= tn; t0++) {
    auto k1 = dt * advection_field.value(p),
         k2 = dt * (advection_field.value(p) + k1 / 2),
         k3 = dt * (advection_field.value(p) + k2 / 2),
         k4 = dt * (advection_field.value(p) + k3);
    return yn += (1 / 6.0) * (k1 + 2 * k2 + 2 * k3 + k4);
    t0 = t0 + dt;
  }
}

///////////////////////////////////////////////////////////////////////////////////////////////

template <int dim> class MeshDeformer {
public:
  MeshDeformer();

  void run();

private:
  void make_grid();

  Triangulation<dim> triangulation;

  GridOut grid_out;
  AdvectionField<dim> advection_field;
  Vector<double> solution;
  RK4<dim> rk4;

  double time;
  double time_step;
  /*!
   * Final simulation time.
   */
  double T_max;
};

template <int dim>
MeshDeformer<dim>::MeshDeformer()
    : triangulation(), time(1.0), time_step(1), T_max(1.0) {}

template <int dim> void MeshDeformer<dim>::make_grid()
{
  GridGenerator::hyper_cube(triangulation, 0, 1, /* colorize faces */
                            false);
  triangulation.refine_global(4);

  std::vector<Tensor<1, dim>> vetices_on_cell;
   		 while (T_max>=0.09) {
  for (const auto &cell : triangulation.active_cell_iterators()) {

    for (unsigned int i = 0; i < GeometryInfo<dim>::vertices_per_cell; i++) {
      Point<2> &v = cell->vertex(i); // initial vertex

      rk4.value(v);

      std::cout << "----------------------------------" << std::endl;

      std::cout << rk4.value(v) << "  rk4    " << std::endl;
      std::cout << "----------------------------------" << std::endl;

      GridOut       grid_out;
       											std::ofstream output("mesh-" + std::to_string(T_max) + ".vtu");
       											grid_out.write_vtu( triangulation, output);
       										 T_max=T_max-time_step;
    }
  }
  }

}

template <int dim> void MeshDeformer<dim>::run()
{
	make_grid();
}

int main()
{

  MeshDeformer<2> rk4_problem_2d;
  rk4_problem_2d.run();

  return 0;
}


/*****************************************************************************
*
* Rokko: Integrated Interface for libraries of eigenvalue decomposition
*
* Copyright (C) 2014 by Synge Todo <wistaria@comp-phys.org>
*
* 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)
*
*****************************************************************************/

// C++ version of TITPACK Ver.2 by H. Nishimori

/************ Sample main program #11 *****************
* 1d Heisenberg antiferromagnet with 8 spins
* Eigenvalues and an eigenvector by diag
******************************************************/

#include <mpi.h>
#include <iostream>
#include <rokko/rokko.hpp>
#include <boost/timer.hpp>
#include "titpack.hpp"
#include "options.hpp"

typedef rokko::parallel_dense_solver solver_type;
typedef rokko::distributed_matrix<rokko::matrix_col_major> matrix_type;

int main(int argc, char** argv) {
  int provided;
  MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided);
  rokko::grid g;

  std::cout.precision(10);
  options opt(argc, argv, 8, solver_type::default_solver(), g.get_myrank() == 0);
  if (!opt.valid) MPI_Abort(MPI_COMM_WORLD, 1);
  boost::timer tm;
  MPI_Barrier(g.get_comm());
  double t1 = tm.elapsed();

  // lattice structure
  int n = opt.N;
  int ibond = n;
  std::vector<int> ipair;
  for (int i = 0; i < ibond; ++i) {
    ipair.push_back(i);
    ipair.push_back((i + 1) % n);
  }

  // Hamiltonian parameters
  std::vector<double> bondwt(ibond, -1);
  std::vector<double> zrtio(ibond, 1);

  // table of configurations and Hamiltonian operator
  subspace ss(n, 0);
  hamiltonian hop(ss, ipair, bondwt, zrtio);
  solver_type solver(opt.solver);
  solver.initialize(argc, argv);

  // Hamiltonian matrix
  matrix_type elemnt(hop.dimension(), hop.dimension(), g, solver);
  elm3(hop, elemnt);
  MPI_Barrier(g.get_comm());
  double t2 = tm.elapsed();

  rokko::localized_vector E(hop.dimension());
  matrix_type v(hop.dimension(), hop.dimension(), g, solver);
  solver.diagonalize(elemnt, E, v);
  double t3 = tm.elapsed();

  if (g.get_myrank() == 0) {
    int ne = 4;
    std::cout << "[Eigenvalues]\n";
    for (int i = 0; i < ne; ++i) std::cout << '\t' << E[i];
    std::cout << std::endl << std::flush;
  }
  MPI_Barrier(g.get_comm());

  // Do not forget to call elm3 again before calling check3
  elm3(hop, elemnt);
  matrix_type w(hop.dimension(), 1, g, solver);
  check3_mpi(elemnt, v, 0, w);
  std::cout << std::flush;
  MPI_Barrier(g.get_comm());
  double t4 = tm.elapsed();

  std::vector<int> npair;
  npair.push_back(1);
  npair.push_back(2);
  std::vector<double> sxx(1);
  matrix_type sxmat(hop.dimension(), hop.dimension(), g, solver);
  xcorr3_mpi(ss, npair, v, 0, sxx, sxmat, w);
  if (v.is_gindex(0, 0)) std::cout << "sxx: " << sxx[0] << std::endl;
  std::cout << std::flush;
  MPI_Barrier(g.get_comm());
  std::vector<double> szz(1);
  zcorr_mpi(ss, npair, v, 0, szz);
  if (g.get_myrank() == 0) std::cout << "szz: " << szz[0] << std::endl;
  std::cout << std::flush;
  MPI_Barrier(g.get_comm());
  double t5 = tm.elapsed();

  if (g.get_myrank() == 0) {
    std::cerr << "initialize      " << (t2-t1) << " sec\n"
              << "diagonalization " << (t3-t2) << " sec\n"
              << "check           " << (t4-t3) << " sec\n"
              << "correlation     " << (t5-t4) << " sec\n";
  }
  MPI_Finalize();
}


#ifndef BIGGLES_PARTITION_SAMPLER_HPP__
#define BIGGLES_PARTITION_SAMPLER_HPP__

#include <boost/tuple/tuple.hpp>

#include "model.hpp"
#include "partition.hpp"
#include "mh_moves/mh_moves.hpp"
#include "sampling/metropolis_hastings.hpp"
#include "sampling/simple.hpp"

namespace biggles {

/// @brief A class which can sample a set of tracks and clutter given a set of model parameters.
///
/// Internally this class is implemented via the Metropolis-Hastings algorithm. See the documentation for sample() for
/// more information.
///
/// This samples from the posterior distribution \f$ P(T | \theta, D) \f$ where \f$ T \f$ is a set of tracks and
/// clutter which encompass all observations, \f$ \theta \f$ is a set of model parameters and \f$ D \f$ are the
/// observations.
///
/// We can re-arrange this posterior using the law of conditional probability which states \f$ P(T | \theta, D) =
/// P(T, \theta | D) / P(\theta | D) \f$ combined with Bayes' theorem:
///
/// \f[
/// P(T, \theta | D)
///     = \frac{P(D|T, \theta) P(T, \theta)}{P(D)}
///     = \frac{P(D|T, \theta) P(T | \theta) P(\theta)}{P(D)}
/// \f]
///
/// and hence
///
/// \f[
/// P(T | \theta, D)
///     = \frac{P(D|T, \theta) P(T | \theta) P(\theta)}{P(\theta | D)P(D)}
///     = \frac{P(D|T, \theta) P(T | \theta) P(\theta)}{P(\theta, D)}.
/// \f]
///
/// Internally we use the Metropolis-Hastings algorithm which requires only a value proportional to this density.
/// Removing terms independent of \f$ T \f$ we obtain:
///
/// \f[
/// P(T | \theta, D) \propto P(D|T, \theta) P(T | \theta) P(\theta).
/// \f]
///
/// The \f$ P(\theta) \f$ term is simply the prior on the model parameters. The \f$ P(T | \theta) \f$ term is a
/// little more subtle; it is the likelihood of the tracks independent of the observations within them. This is
/// a function of the model parameters and birth and death times for the tracks only. Since the data is partitioned
/// between tracks and the clutter with no overlap, we may factorise the data likelihood term as follows:
///
/// \f[
/// P(D | T, \theta) = P(d_0 | \theta) \prod_{i = 1}^K P(d_i | t_i, \theta)
/// \f]
///
/// where \f$ d_0 \f$ is used to represent the clutter observations and \f$ d_1, ..., d_K \f$ are the \f$ K \f$
/// sets of observations corresponding to the \f$ K \f$ tracks in the partition. \f$ P(d_0 | \theta) \f$ is the
/// likelihood of having seen the clutter observations we have given the parameters. \f$ P(d_i | t_i, \theta) \f$ is
/// the likelihood of track \f$ i \f$ having generated the data we saw.
///
/// Taking logarithms, we obtain the final log density function used within the Metropolis-Hastings sampler:
///
/// \f[
/// \ell(T | \theta, D) = \kappa + \ell(d_0 | t_0, \theta) + \sum_{i = 1}^K \ell(d_i | t_i, \theta) + \ell(T | \theta) + \ell(\theta)
/// \f]
///
/// where \f$ \ell(\cdot) \f$ is used to denote the log-PDF and \f$ \kappa \f$ is some arbitrary normalising offset.
/// Without loss of generality, we set it to zero.
///
/// Each log-PDF term in the expansion above is calculated by one of the log-PDF functions in biggles/model.hpp.
class partition_sampler : public sampling::metropolis_hastings_sampler
{
private:
    typedef sampling::metropolis_hastings_sampler base_type;
public:
    /// @brief Construct a sampler from an initial partition and set of model parameters.
    ///
    /// @param initial_partition
    /// @param initial_parameters
    partition_sampler(const partition_ptr_t& initial_partition_ptr,
                      const model::parameters& initial_parameters = model::parameters())
        : base_type(partition_distribution(initial_parameters),
                    partition_proposal(),
                    partition_sampler_sample(initial_partition_ptr, mh_moves::NONE, mh_moves::NONE))
    {
        // Merge partition sampler and MH sampler?
    }

    /// @brief The model parameters associated with this sampler.
    const model::parameters& parameters() const { return target_.params; }

    /// @brief Return a reference to the partition of the last sample drawn so far.
    const partition_ptr_t last_partition() const { return last_sample().partition_sample_ptr; }

    /// @brief Return a reference to the move type of the last sample drawn so far.
    const mh_moves::move_type& last_move() const { return last_sample().executed_move; }

    /// @brief Modify the parameters associated with this sampler.
    ///
    /// @param params A new set of parameters for the sampler.
    void set_parameters(const model::parameters& params) { target_.params = params; }

    /// @brief set the partition_is_valid value
    void set_partition_is_valid(bool v) { propose_.partition_is_valid = v; }
};

} // namespace biggles

#endif //BIGGLES_PARTITION_SAMPLER_HPP__


#ifndef nomad__src__matrix__functions__multiply_hpp
#define nomad__src__matrix__functions__multiply_hpp

#include <Eigen/Core>

#include <src/matrix/functions/dot.hpp>
#include <src/var/var.hpp>

namespace nomad {

  template<typename DerivedA, typename DerivedB>
  inline typename std::enable_if<
    is_var<typename Eigen::MatrixBase<DerivedA>::Scalar>::value &&
    is_var<typename Eigen::MatrixBase<DerivedB>::Scalar>::value &&
    std::is_same<typename Eigen::MatrixBase<DerivedA>::Scalar,
                 typename Eigen::MatrixBase<DerivedB>::Scalar>::value,
    typename Eigen::Matrix<typename Eigen::MatrixBase<DerivedA>::Scalar,
             Eigen::Dynamic, Eigen::Dynamic>
    >::type
  multiply(const Eigen::MatrixBase<DerivedA>& M1,
           const Eigen::MatrixBase<DerivedB>& M2) {

    const eigen_idx_t N = M1.cols();
    Eigen::Matrix<typename Eigen::MatrixBase<DerivedA>::Scalar, Eigen::Dynamic, Eigen::Dynamic> M(N, N);

    for (eigen_idx_t i = 0; i < N; ++i) {
      for (eigen_idx_t j = 0; j < N; ++j) {
        M(i, j) = dot(M1.row(i), M2.col(j));
      }
    }

    return M;

  }

}

#endif


/*
// Copyright 2018 University of Liege
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Authors:
// - Adrien Crovato
*/

//// Field mapping
// Identify each cell as an interior or exterior (FLAG 0, 1). Also create vector of indexes associated to
// field mapping type.
// Crossing number method is used as point in polygon algorithm.
//
// References: http://geomalgorithms.com/a03-_inclusion.html
//
// I/O:
// - sGrid: temporary dynamic array containing body panel vertices
// - numC: list of numerical parameters (structure)
// - bPan: body panels (structure)
// - fPan: field panels (structure)

#include <iostream>
#include <Eigen/Dense>
#include "map_field.h"

#define NDIM 3

using namespace std;
using namespace Eigen;

void map_field(MatrixX3d &sGrid, Numerical_CST &numC, Network &bPan, Field &fPan) {

    //// Begin
    // Temporary variables
    double minX, maxX, minY, maxY, minZ, maxZ; // minimal bounding box holding the geometry
    int iE = 0, iI = 0; // indexes
    MatrixX2d panVert;
    panVert.resize(bPan.nC,2);

    // Display check
    cout << "Mapping field cells... " << flush;

    // Resize matrices
    fPan.fMap.resize(fPan.nF);

    // Compute minimum size box including full geometry
    minX = sGrid.col(0).minCoeff() - numC.TOLB;
    maxX = sGrid.col(0).maxCoeff() + numC.TOLB;
    minY = sGrid.col(1).minCoeff();
    maxY = sGrid.col(1).maxCoeff();
    minZ = sGrid.col(2).minCoeff() - numC.TOLB;
    maxZ = sGrid.col(2).maxCoeff() + numC.TOLB;

    //// Map field cells
    for (int f = 0; f < fPan.nF; f++) {
        lbl_loop0:
        // Bounding box
        if (fPan.CG(f,0) < minX || fPan.CG(f,0) > maxX
            || fPan.CG(f,1) < minY || fPan.CG(f,1) > maxY
            || fPan.CG(f,2) < minZ || fPan.CG(f,2) > maxZ) {
            fPan.fMap(f) = 1;
            fPan.nE++;
        }
        // 2D PIP algorithm
        else {
            // Find spanwise station corresponding to y-coordinate of field cell
            for (int s = 0; s < bPan.nS_; s++) {
                if (fPan.CG(f,1) <= sGrid((s+1)*bPan.nC,1)) {
                    // Interpolate
                    double a = (sGrid((s+1)*bPan.nC,1) - fPan.CG(f,1)) / (sGrid((s+1)*bPan.nC,1) - sGrid(s*bPan.nC,1));
                    double b = (fPan.CG(f,1) - sGrid(s*bPan.nC,1)) / (sGrid((s+1)*bPan.nC,1) - sGrid(s*bPan.nC,1));
                    // Store interpolated airfoil panel vertices
                    for (int i = 0; i < bPan.nC; i++) {
                        panVert(i,0) = a*sGrid(s*bPan.nC+i,0) + b*sGrid((s+1)*bPan.nC+i,0);
                        panVert(i,1) = a*sGrid(s*bPan.nC+i,2) + b*sGrid((s+1)*bPan.nC+i,2);
                    }
                    // Crossing number method
                    int nInter = 0;
                    for (int i = 0; i < bPan.nC_; i++) {
                        // Check if point is not on a vertex
                        if (fPan.CG(f,0) == panVert(i,0) && fPan.CG(f,2) == panVert(i,1)) {
                            fPan.fMap(f) = 0;
                            fPan.nI++;
                            f++;
                            goto lbl_loop0; // *might wanna use goto here!!
                        }
                        // Exclude top endpoint of segment to avoid double crossings and horizontal edges
                        if ((fPan.CG(f,2) >= panVert(i,1) && fPan.CG(f,2) < panVert(i+1,1))
                            || (fPan.CG(f,2) < panVert(i,1) && fPan.CG(f,2) >= panVert(i+1,1))) {
                            double sI = (fPan.CG(f,2) - panVert(i,1)) / (panVert(i+1,1) - panVert(i,1)); // Compute y-intersection
                            if (fPan.CG(f,0) <  panVert(i,0) + sI * (panVert(i+1,0) - panVert(i,0))) { // If point is left of x-intersect
                                nInter++; // Then intersection is valid
                            }
                        }
                    }
                    // Even number of intersections, external point
                    if (nInter%2 == 0) {
                        fPan.fMap(f) = 1;
                        fPan.nE++;
                        break;
                    }
                    // Odd number of intersections, internal point
                    else {
                        fPan.fMap(f) = 0;
                        fPan.nI++;
                        break;
                    }
                }
                else
                    continue;
            }
        }
    }
    // Find cell indices according to mapping type
    fPan.eIdx.resize(fPan.nE);
    fPan.iIdx.resize(fPan.nI);
    for (int f = 0; f < fPan.nF; ++f) {
        if (fPan.fMap(f)) {
            fPan.eIdx(iE) = f;
            iE++;
        }
        else {
            fPan.iIdx(iI) = f;
            iI++;
        }
    }

    //// Control display
    cout << "Done!" << endl;
    cout << "Field map: " << fPan.fMap.size() << endl;
    #ifdef VERBOSE
        for (int j = 0; j < fPan.nY; ++j) {
            cout << endl;
            for (int k = 0; k < fPan.nZ; ++k) {
                cout << endl;
                for (int i = 0; i < fPan.nX; ++i)
                    cout << fPan.fMap(i + k * fPan.nX + j * fPan.nX * fPan.nZ) << ' ';
            }
        }
        cout << endl;
    #endif
    cout << "Exterior cells: " << fPan.eIdx.rows() << endl;
    cout << "Interior cells: " << fPan.iIdx.rows() << endl;
    cout << endl;
}


#pragma once
// eigen
#include <Eigen/Core>
#include <Eigen/Dense>
#include <unsupported/Eigen/NonLinearOptimization>
#include <unsupported/Eigen/NumericalDiff>
// opencv
#include "opencv2/opencv.hpp"
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
// std
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
#include <thread>
#include <future>
#define GLM_FORCE_RADIANS
#include <glm/detail/type_mat.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "timer.hpp"
#define MARKER 4
using namespace std;
using namespace Eigen;

typedef Matrix<double, 4, MARKER> Matrix4xMARKERd;
typedef Matrix<double, 3, MARKER> Matrix3xMARKERd;
typedef Matrix<double, 3, 4> Matrix3x4d;

// Generic functor for Eigen Levenberg-Marquardt minimizer
template<typename _Scalar, int NX=Dynamic, int NY=Dynamic>
struct Functor
{
    typedef _Scalar Scalar;
    enum {
        InputsAtCompileTime = NX,
        ValuesAtCompileTime = NY
    };
    typedef Matrix<Scalar,InputsAtCompileTime,1> InputType;
    typedef Matrix<Scalar,ValuesAtCompileTime,1> ValueType;
    typedef Matrix<Scalar,ValuesAtCompileTime,InputsAtCompileTime> JacobianType;

    const int m_inputs, m_values;

    Functor() : m_inputs(InputsAtCompileTime), m_values(ValuesAtCompileTime) {}
    Functor(int inputs, int values) : m_inputs(inputs), m_values(values) {}

    int inputs() const { return m_inputs; }
    int values() const { return m_values; }
};

struct WebcamMarkerModel:Functor<double>{
    WebcamMarkerModel();
    void initializeModel();
    void checkCorrespondence();
    void projectInto2D(Matrix3xMARKERd &position2d, Matrix4xMARKERd &position3d, Matrix3x4d &RT);
    void getRTmatrix(VectorXd &x, Matrix3x4d &RT);
    void getRTmatrix(Matrix4d &RT);
    void getRTmatrix(Matrix4f &RT);
    bool track();
    int operator()(const VectorXd &x, VectorXd &fvec) const;
    VectorXd pose;
    Matrix4d ModelMatrix;
    Matrix4d Trafo2FirstCamera;
    Matrix4xMARKERd pos3D;// ((x y z 1)' (x y z 1)' ... ),
    Vector4d origin3D;
    Vector3d origin2D;
    Matrix3xMARKERd pos2D;// ((x y 1)' (x y 1)' ... )
    cv::Mat img, img_rectified;
    cv::Mat map1, map2;
    cv::VideoCapture capture;
    uint id;
    char name[20];
    Matrix3d K;
    int threshold_value = 240;
    std::vector<uint> markerIDs;
    NumericalDiff<WebcamMarkerModel> *numDiff;
    Eigen::LevenbergMarquardt<Eigen::NumericalDiff<WebcamMarkerModel>, double> *lm;
    double reprojectionError;
};

class MarkerTracker{
public:
    /**
     * Constructor
     * @param devices vector of camera ids
     */
    MarkerTracker(std::vector<int> &devices);
    ~MarkerTracker();

    void poseEstimation();
    bool init();
    bool findTrafoBetweenCameras();

    void predictMarkerPosition3D();
    void updateWithMeasurement(Vector3d &z);

    Matrix4d ModelMatrix;
    Vector3d pos3D_old, pos3D_new;

    std::vector<WebcamMarkerModel> webcam;
    bool stopPoseTracking = false;
    std::thread* pose_estimation_thread = nullptr;
private:
    Matrix3d Q,M,P,R,Kgain;
    Timer timer;
};


// Copyright Daniel Wallin 2006. 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 <boost/test/unit_test.hpp>
#include <boost/time_series/numeric/coarse_grain.hpp>
#include <boost/time_series/piecewise_constant_series.hpp>
#include <boost/time_series/dense_series.hpp>

using namespace boost;
using namespace unit_test;

void test_coarse_grain()
{
    using namespace boost::time_series;

    sparse_series<int> s1;

    make_ordered_inserter(s1)
        (1, 0)(2)(3)(4)(5)(6)(7)
    .commit();

    sparse_series<int> r1(discretization = 3);

    make_ordered_inserter(r1)
        (1, 0)(4)(7)
    .commit();

    BOOST_CHECK_EQUAL(r1, coarse_grain(s1, 3));

    sparse_series<int> r2(discretization = 2);

    make_ordered_inserter(r2)
        (1, 0)(3)(5)(7)
    .commit();

    BOOST_CHECK_EQUAL(r2, coarse_grain(s1, 2));

    sparse_series<int> s2(discretization = 10);

    make_ordered_inserter(s2)
        (1, 2, 4)
        (2, 6, 8)
    .commit();

    sparse_series<int> r3(discretization = 50);

    make_ordered_inserter(r3)
        (0, 0)(0)
    .commit();

    BOOST_CHECK_EQUAL(r3, coarse_grain(s2, 50));

    piecewise_constant_series<int> s3(discretization = 30);

    make_ordered_inserter(s3)
        (1, 2, 4)
        (2, 6, 8)
    .commit();

    sparse_series<int> r4(discretization = 60);

    make_ordered_inserter(r4)
        (0, 0)(1)(0)(2)(0)
    .commit();

    BOOST_CHECK_EQUAL(r4, coarse_grain(s3, 60));

    dense_series<int> d(stop = 100, value = 2);

    sparse_series<int> r5(discretization = 10);

    make_ordered_inserter(r5)
        (2, 0)(2)(2)(2)(2)(2)(2)(2)(2)(2)
    .commit();

    BOOST_CHECK_EQUAL(r5, coarse_grain(d, 10));

    piecewise_constant_series<int> s4;

    make_ordered_inserter(s4)
        (1, 0, 3)
        (3, 8, 11)
    .commit();

    sparse_series<int> r6(discretization = 2);

    make_ordered_inserter(r6)
        (1, 0)(1)(0)(0)(3)(3)
    .commit();

    BOOST_CHECK_EQUAL(r6, coarse_grain(s4, 2));

    dense_series<int, mpl::int_<10> > s5;
    make_ordered_inserter(s5)
        (1, 0)(2)(3)(4)
    .commit();

    sparse_series<int, mpl::int_<20> > r7;

    make_ordered_inserter(r7)
        (1, 0)(3)
    .commit();

    BOOST_CHECK_EQUAL(r7, coarse_grain(s5, mpl::int_<20>()));

    sparse_series<int> o4(discretization = 60);
    ordered_inserter<sparse_series<int> > out(o4);

    coarse_grain(s3, 60, out).commit();
    BOOST_CHECK_EQUAL(r4, o4);
}

///////////////////////////////////////////////////////////////////////////////
// init_unit_test_suite
//
test_suite* init_unit_test_suite( int argc, char* argv[] )
{
    test_suite *test = BOOST_TEST_SUITE("coarse_grain test");

    test->add(BOOST_TEST_CASE(&test_coarse_grain));

    return test;
}


// Copyright (c) 2015-2020 Daniel Cooke
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

#include "coalescent_model.hpp"

#include <memory>
#include <cmath>
#include <complex>
#include <numeric>
#include <stdexcept>

#include <boost/math/special_functions/binomial.hpp>

#include "utils/maths.hpp"

namespace octopus {

CoalescentModel::CoalescentModel(Haplotype reference, Parameters params,
                                 std::size_t num_haplotyes_hint, CachingStrategy caching)
: reference_ {std::move(reference)}
, indel_heterozygosity_model_ {make_indel_model(reference_, {params.indel_heterozygosity})}
, params_ {params}
, haplotypes_ {}
, caching_ {caching}
, index_cache_ {}
, index_flag_buffer_ {}
, k_indel_zero_result_cache_ {2 * num_haplotyes_hint, std::vector<boost::optional<LogProbability>> {}}
{
    if (params_.snp_heterozygosity <= 0 || params_.indel_heterozygosity <= 0) {
        throw std::domain_error {"CoalescentModel: snp and indel heterozygosity must be > 0"};
    }
    site_buffer1_.reserve(128);
    site_buffer2_.reserve(128);
    if (caching == CachingStrategy::address) {
        difference_address_cache_.reserve(num_haplotyes_hint);
    } else if (caching_ == CachingStrategy::value) {
        difference_value_cache_.reserve(num_haplotyes_hint);
        difference_value_cache_.emplace(std::piecewise_construct,
                                        std::forward_as_tuple(reference_),
                                        std::forward_as_tuple());
    }
    k_indel_pos_result_cache_.reserve(2 * num_haplotyes_hint);
}

void CoalescentModel::set_reference(Haplotype reference)
{
    reference_ = std::move(reference);
    if (caching_ == CachingStrategy::address) {
        difference_address_cache_.clear();
    } else if (caching_ == CachingStrategy::value) {
        difference_value_cache_.clear();
        difference_value_cache_.emplace(std::piecewise_construct,
                                        std::forward_as_tuple(reference_),
                                        std::forward_as_tuple());
    }
}

void CoalescentModel::prime(MappableBlock<Haplotype> haplotypes)
{
    haplotypes_ = std::move(haplotypes);
    index_cache_.assign(haplotypes_.size(), boost::none);
    index_flag_buffer_.assign(haplotypes_.size(), false);
}

void CoalescentModel::unprime() noexcept
{
    haplotypes_.clear();
    haplotypes_.shrink_to_fit();
    index_cache_.clear();
    index_cache_.shrink_to_fit();
    index_flag_buffer_.clear();
    index_flag_buffer_.shrink_to_fit();
}

bool CoalescentModel::is_primed() const noexcept
{
    return !index_cache_.empty();
}

CoalescentModel::LogProbability CoalescentModel::evaluate(const Haplotype& haplotype) const
{
    return evaluate(count_segregating_sites(haplotype));
}

CoalescentModel::LogProbability CoalescentModel::evaluate(const std::vector<unsigned>& haplotype_indices) const
{
    return evaluate(count_segregating_sites(haplotype_indices));
}

namespace {

auto powm1(const unsigned i) noexcept // std::pow(-1, i)
{
    return (i % 2 == 0) ? 1 : -1;
}

auto binom(const unsigned n, const unsigned k)
{
    return boost::math::binomial_coefficient<CoalescentModel::LogProbability>(n, k);
}

auto log_binom(const unsigned n, const unsigned k)
{
    using T = CoalescentModel::LogProbability;
    using maths::log_factorial;
    return log_factorial<T>(n) - (log_factorial<T>(k) + log_factorial<T>(n - k));
}

template <typename T>
auto coalescent_real_space(const unsigned n, const unsigned k, const T theta)
{
    T result {0};
    for (unsigned i {2}; i <= n; ++i) {
        result += powm1(i) * binom(n - 1, i - 1) * ((i - 1) / (theta + i - 1)) * std::pow(theta / (theta + i - 1), k);
    }
    return std::log(result);
}

template <typename ForwardIt>
auto complex_log_sum_exp(ForwardIt first, ForwardIt last)
{
    using ComplexType = typename std::iterator_traits<ForwardIt>::value_type;
    const auto l = [] (const auto& lhs, const auto& rhs) { return lhs.real() < rhs.real(); };
    const auto max = *std::max_element(first, last, l);
    return max + std::log(std::accumulate(first, last, ComplexType {},
                                          [max] (const auto curr, const auto x) { return curr + std::exp(x - max); }));
}

template <typename Container>
auto complex_log_sum_exp(const Container& logs)
{
    return complex_log_sum_exp(std::cbegin(logs), std::cend(logs));
}

template <typename T>
auto coalescent_log_space(const unsigned n, const unsigned k, const T theta)
{
    std::vector<std::complex<T>> tmp(n - 1, std::log(std::complex<T> {-1}));
    for (unsigned i {2}; i <= n; ++i) {
        auto& cur = tmp[i - 2];
        cur *= i;
        cur += log_binom(n - 1, i - 1);
        cur += std::log((i - 1) / (theta + i - 1));
        cur += k * std::log(theta / (theta + i - 1));
    }
    return complex_log_sum_exp(tmp).real();
}

template <typename T>
auto coalescent(const unsigned n, const unsigned k, const T theta)
{
    if (n < 30 && k <= 80) {
        auto result = coalescent_real_space(n, k, theta);
        if (std::isnan(result)) {
            result = coalescent_log_space(n, k, theta);
        }
        return result;
    } else {
        return coalescent_log_space(n, k, theta);
    }
}

template <typename T>
auto coalescent(const unsigned n, const unsigned k_snp, const unsigned k_indel,
                const T theta_snp, const T theta_indel)
{
    const auto theta = theta_snp + theta_indel;
    const auto k_tot = k_snp + k_indel;
    auto result = coalescent(n, k_tot, theta);
    result += k_snp * std::log(theta_snp / theta);
    result += k_indel * std::log(theta_indel / theta);
    result += log_binom(k_tot, k_snp);
    return result;
}

} // namespace

CoalescentModel::LogProbability CoalescentModel::evaluate(const SiteCountTuple& t) const
{
    unsigned k_snp, k_indel, n;
    std::tie(k_snp, k_indel, n) = t;
    if (k_indel == 0) {
        return evaluate(k_snp, n);
    } else {
        return evaluate(k_snp, k_indel, n);
    }
}

CoalescentModel::LogProbability CoalescentModel::evaluate(const unsigned k_snp, const unsigned n) const
{
    if (k_indel_zero_result_cache_.size() > n) {
        if (k_indel_zero_result_cache_[n].size() > k_snp) {
            auto& result = k_indel_zero_result_cache_[n][k_snp];
            if (!result) {
                result = coalescent(n, k_snp, 0, params_.snp_heterozygosity, params_.indel_heterozygosity);
            }
            return *result;
        } else {
            k_indel_zero_result_cache_[n].resize(k_snp + 1, boost::none);
        }
    } else {
        k_indel_zero_result_cache_.resize(n + 1);
        k_indel_zero_result_cache_[n].assign(k_snp + 1, boost::none);
    }
    const auto result = coalescent(n, k_snp, 0, params_.snp_heterozygosity, params_.indel_heterozygosity);
    k_indel_zero_result_cache_[n][k_snp] = result;
    return result;
}

CoalescentModel::LogProbability CoalescentModel::evaluate(const unsigned k_snp, const unsigned k_indel, const unsigned n) const
{
    const auto indel_heterozygosity = calculate_buffered_indel_heterozygosity();
    const auto t = std::make_tuple(k_snp, k_indel, n, maths::round_sf(indel_heterozygosity, 6));
    auto itr = k_indel_pos_result_cache_.find(t);
    if (itr != std::cend(k_indel_pos_result_cache_)) {
        return itr->second;
    }
    const auto result = coalescent(n, k_snp, k_indel, params_.snp_heterozygosity, indel_heterozygosity);
    k_indel_pos_result_cache_.emplace(t, result);
    return result;
}

void CoalescentModel::fill_site_buffer(const Haplotype& haplotype) const
{
    assert(site_buffer2_.empty());
    site_buffer1_.clear();
    if (caching_ == CachingStrategy::address) {
        fill_site_buffer_from_address_cache(haplotype);
    } else {
        fill_site_buffer_from_value_cache(haplotype);
    }
    site_buffer1_ = std::move(site_buffer2_);
    site_buffer2_.clear();
}

void CoalescentModel::fill_site_buffer_uncached(const Haplotype& haplotype) const
{
    // Although we won't retrieve from the cache, we need to make sure all the variants
    // stay in existence as we populate the buffers by reference.
    auto itr = difference_value_cache_.find(reference_);
    if (itr == std::cend(difference_value_cache_)) {
        itr = difference_value_cache_.emplace(reference_, haplotype.difference(reference_)).first;
    } else {
        itr->second = haplotype.difference(reference_);
    }
    std::set_union(std::begin(site_buffer1_), std::end(site_buffer1_),
                   std::cbegin(itr->second), std::cend(itr->second),
                   std::back_inserter(site_buffer2_));
}

void CoalescentModel::fill_site_buffer_from_value_cache(const Haplotype& haplotype) const
{
    auto itr = difference_value_cache_.find(haplotype);
    if (itr == std::cend(difference_value_cache_)) {
        itr = difference_value_cache_.emplace(haplotype, haplotype.difference(reference_)).first;
    }
    std::set_union(std::begin(site_buffer1_), std::end(site_buffer1_),
                   std::cbegin(itr->second), std::cend(itr->second),
                   std::back_inserter(site_buffer2_));
}

void CoalescentModel::fill_site_buffer_from_address_cache(const Haplotype& haplotype) const
{
    auto itr = difference_address_cache_.find(std::addressof(haplotype));
    if (itr == std::cend(difference_address_cache_)) {
        itr = difference_address_cache_.emplace(std::addressof(haplotype), haplotype.difference(reference_)).first;
    }
    std::set_union(std::begin(site_buffer1_), std::end(site_buffer1_),
                   std::cbegin(itr->second), std::cend(itr->second),
                   std::back_inserter(site_buffer2_));
}

CoalescentModel::SiteCountTuple CoalescentModel::count_segregating_sites(const Haplotype& haplotype) const
{
    fill_site_buffer(haplotype);
    return count_segregating_sites_in_buffer(1);
}

CoalescentModel::SiteCountTuple CoalescentModel::count_segregating_sites_in_buffer(const unsigned num_haplotypes) const
{
    const auto num_indels = std::count_if(std::cbegin(site_buffer1_), std::cend(site_buffer1_),
                                          [] (const auto& v) noexcept { return is_indel(v); });
    return std::make_tuple(site_buffer1_.size() - num_indels, num_indels, num_haplotypes + 1);
}

double CoalescentModel::calculate_buffered_indel_heterozygosity() const
{
    boost::optional<double> max_heterozygosity {};
    for (const auto& site : site_buffer1_) {
        if (is_indel(site)) {
            auto site_heterozygosity = calculate_heterozygosity(site);
            if (max_heterozygosity) {
                max_heterozygosity = std::max(*max_heterozygosity, site_heterozygosity);
            } else {
                max_heterozygosity = site_heterozygosity;
            }
        }
    }
    return max_heterozygosity ? *max_heterozygosity : params_.indel_heterozygosity;
}

double CoalescentModel::calculate_heterozygosity(const Variant& indel) const
{
    assert(is_indel(indel));
    const auto offset = static_cast<std::size_t>(begin_distance(reference_, indel));
    return calculate_indel_probability(indel_heterozygosity_model_, offset, indel_size(indel));
}

CoalescentProbabilityGreater::CoalescentProbabilityGreater(CoalescentModel model)
: model_ {std::move(model)}
, buffer_ {}
, cache_ {}
{
    buffer_.reserve(1);
    cache_.reserve(100);
}

bool CoalescentProbabilityGreater::operator()(const Haplotype& lhs, const Haplotype& rhs) const
{
    if (have_same_alleles(lhs, rhs)) return true;
    auto cache_itr = cache_.find(lhs);
    if (cache_itr == std::cend(cache_)) {
        buffer_.assign({lhs});
        cache_itr = cache_.emplace(lhs, model_.evaluate(buffer_)).first;
    }
    const auto lhs_probability = cache_itr->second;
    cache_itr = cache_.find(rhs);
    if (cache_itr == std::cend(cache_)) {
        buffer_.assign({rhs});
        cache_itr = cache_.emplace(rhs, model_.evaluate(buffer_)).first;
    }
    const auto rhs_probability = cache_itr->second;
    return lhs_probability > rhs_probability;
}

} // namespace octopus


#include <iostream>
#include <fstream>
#include <Eigen/Eigen>
//include the bie header files
#include "material.hh"
#include "precomputed_kernel.hh"
#include "bimat_interface.hh"
#include "infinite_boundary.hh"
//include the fem header files
//#include "mesh_Generated.hpp"
#include "mesh_Generated_multi_faults.hpp"

#include "bcdof.hpp"
#include "bcdof_ptr.hpp"
#include "cal_ke.hpp"
#include "cal_fe_global_const_ke.hpp"
#include "mapglobal.hpp"
#include "Slip_Weakening.hpp"
#include "Rate_and_State_Aging.hpp"
#include "cal_slip_sliprate.hpp"
#include "update_disp_velocity.hpp"
#include "time_advance.hpp"
#include "BIE_correct.hpp"

using namespace Eigen;
using namespace std;

int main() {
    // Domain Size
    double x_min = -20e3;
    double x_max = 20e3;
    double y_min = -1.0e3;
    double y_max = 1.0e3;
    int dim = 2.0;
    double dx = 50;
    double dy = 50;
    int nx_el = (x_max-x_min)/dx;
    int ny = (y_max-y_min)/dy;
    MatrixXd Node = MatrixXd::Zero((nx_el+1)*(ny+1),2);
    MatrixXi_rm Element(nx_el*ny,4); Element.setZero();
    ArrayXi BIE_top_surf_nodes = ArrayXi::Zero((nx_el+1),1);
    ArrayXi BIE_bot_surf_nodes = ArrayXi::Zero((nx_el+1),1);
    int num_faults = 1;
    // Mesh
    // Position of the fault : y_pos, x_left, x_right
    MatrixXd fault_pos(num_faults,3);
    fault_pos << 0.0,  x_min ,x_max;

    //    MatrixXd fault_pos;
    //    read_matrix("fault_pos.txt", fault_pos);
    //    int num_faults = fault_pos.rows();
    cout<<"test_mat="<<"\n"<<fault_pos<<"\n"<<"rows="<<fault_pos.rows()<<"\n";
    std::vector<std::vector<int>> fault_nodes(num_faults*2);
    mesh_Generated_multi_faults(x_min,x_max,y_min,y_max,dx,dy,nx_el,ny,Node, Element,BIE_top_surf_nodes, BIE_bot_surf_nodes,fault_pos, fault_nodes);

     double Vw_width = 5e3;
    // Finding Nodes on the diagonal
    ArrayXi left_diag_nodes =ArrayXi::Zero((ny+1),1);
    ArrayXi right_diag_nodes =ArrayXi::Zero((ny+1),1); ;
    double TOL = 1e-6;
    int m = 0;
    int n = 0;
    for(int i=0; i<Node.rows(); i++)
    {
        if ((std::abs(Node(i,0) +Vw_width/2.0)<TOL)&&(std::abs(Node(i,1))>=dx))
        {
            left_diag_nodes(m) = i ;
            m+=1;
        };
        if ((std::abs(Node(i,0) - Vw_width/2.0)<TOL)&&(std::abs(Node(i,1))>=dx))
        {
            right_diag_nodes(n) = i ;
            n+=1;
        };
    }
    VectorXi left_diag_index = VectorXi::Zero(2*(left_diag_nodes.size()),1);
    VectorXi right_diag_index = VectorXi::Zero(2*(right_diag_nodes.size()),1);

    bcdof(left_diag_nodes,dim,left_diag_index);
    bcdof(right_diag_nodes,dim,right_diag_index);

//    std::cout<<left_diag_nodes<<std::endl;
//    std::cout<<"********"<<std::endl;
//    std::cout<<right_diag_nodes<<std::endl;

    std::ofstream Element_output("results/Element.txt");
    std::ofstream Node_output("results/Node.txt");
    Node_output<<Node;
    Element_output<<Element;

    Element_output.close();
    Node_output.close();
    // Vector containing number of elements on each faults (nx)
    std::vector<int> nx_faults(num_faults);
    for (int i=0;i<num_faults;i++)
    {
        nx_faults[i] =fault_nodes[2*i].size();
    }

    // Write the x coordiantes for each fault
    std::vector<double> fault_angle(num_faults);
    double x1 = 0.0;
    double x2 = 0.0;
    double y1 = 0.0;
    double y2 = 0.0;
    for (int j=0;j<num_faults; j++)
    {
        VectorXd x = VectorXd::Zero(nx_faults[j], 1);
        VectorXd y = VectorXd::Zero(nx_faults[j], 1);

        for (int i=0; i<x.size(); i++)
        {
            x(i) = Node(fault_nodes[2*j][i],0);
            y(i) = Node(fault_nodes[2*j][i],1);
        }

        //std::ofstream x_output("results/fault_x_coord.txt");
        std::string  x_fault= "results/x_fault_"+std::to_string(j)+".txt";
        std::ofstream x_output(x_fault);
        x_output<<x;

        x1 = Node(fault_nodes[2*j][0],0);
        x2 = Node(fault_nodes[2*j][nx_faults[j]-1],0);
        y1 = Node(fault_nodes[2*j][0],1);
        y2 = Node(fault_nodes[2*j][nx_faults[j]-1],1);
        fault_angle[j]=std::atan2(y2-y1,x2-x1);
    }
    int n_nodes = Node.rows();
    int n_el = Element.rows();
    int Ndofn = 2;
    int Nnel = Element.cols();
   // nx_el = fault_nodes.size()-1;
    // Material
    double density = 2670.0;
    double v_s =3.464e3;
    double v_p = 6.0e3;
    double G= pow(v_s,2)*density;
    double Lambda = pow(v_p,2)*density-2.0*G;
    double E  = G*(3.0*Lambda+2.0*G)/(Lambda+G);
    double nu = Lambda/(2.0*(Lambda+G));
    // Time
    double alpha = 0.1;
    double dt = alpha*dx/v_p;
    // Reyleigh Damping
    double beta =0.2;
    double q = beta*dt;
    double time_run = 9.0;
    int numt = time_run/dt;
    //numt =1;

    std::ofstream time_output("results/time.txt");
    time_output<<time_run<<"\n";
    time_output<<dt<<"\n";
    time_output<<numt<<std::endl;
    //numt = 3;
    VectorXd time = dt*VectorXd::LinSpaced(numt,1,numt);
    // Rate and state friction parameters

    double a = 0.008;
    double b = 0.012;

    VectorXd a_array = a*VectorXd::Zero(nx_el+1,1);
    VectorXd b_array = b*VectorXd::Zero(nx_el+1,1);

    double V_0 = 1e-6;
    double f_0 = 0.6;
    double L = 0.02;
    double tau_ini = 75e6;
    double sigma_ini = 120e6;
    double V_ini  = 1e-12;
    // Intialization
    // disp velocity current and next time step (new)
    VectorXd u_n = VectorXd::Zero(n_nodes*Ndofn,1);
    VectorXd v_n = VectorXd::Zero(n_nodes*Ndofn,1);
    VectorXd u_new = VectorXd::Zero(n_nodes*Ndofn,1);
    VectorXd v_new = VectorXd::Zero(n_nodes*Ndofn,1);
    VectorXd a_n = VectorXd::Zero(n_nodes*Ndofn,1);
    // slip and slip-rate
    VectorXd delt_u_n = VectorXd::Zero(Ndofn*(nx_el+1),1);
    VectorXd delt_v_n = VectorXd::Zero(Ndofn*(nx_el+1),1);
    std::cout<<delt_u_n.size()<<std::endl;
    std::cout<<nx_el<<std::endl;
    // Stress on the fault T_0 = intial stress , T = sticking force, T_c= stress critical goes into F_global
    VectorXd T_0 = VectorXd::Zero(Ndofn*(nx_el+1),1);
    VectorXd T = VectorXd::Zero(Ndofn*(nx_el+1),1);
    VectorXd T_c = VectorXd::Zero(Ndofn*(nx_el+1),1);
    VectorXd tau_s = VectorXd::Zero(nx_el+1,1);
    VectorXd F_ext_global = VectorXd::Zero(n_nodes*Ndofn,1);
    VectorXd theta_n =VectorXd::Zero(nx_el+1, 1);
    VectorXd theta_dot_n = VectorXd::Zero(nx_el+1, 1);
    VectorXd theta_ini =VectorXd::Zero(nx_el+1, 1);

    // Setting intial stress on the fault
    for (int i=0; i<T_0.size()/2; i++)
    {
        T_0(2*i) = tau_ini;
        T_0(2*i+1) = -sigma_ini;
    }
    VectorXd x = VectorXd::LinSpaced(nx_el+1,x_min,x_max);
    double d_tau_0 = 25e6;
    double R = 2.5e3;
    VectorXd F_r = VectorXd::Zero(nx_el+1,1);
    for (int i=0; i<nx_el+1; i++)
    {
        if (fabs(x(i))<R)
        {
            F_r(i) = exp(pow(x(i),2)/( pow(x(i),2)-pow(R,2)));
        }
        else
        {
            F_r(i) = 0.0;
        }
    }
    VectorXd G_t = VectorXd::Zero(numt,1);
    double T_ramp = 1.0;
    for (int i=0; i<numt;i++)
    {
        if(time(i)<T_ramp)
        {
            G_t(i) = exp(pow((time(i)-T_ramp),2)/(time(i)*(time(i)-2*T_ramp)));
        }
        else
        {
            G_t(i) = 1.0;
        }
    }

    for (int i =0; i<nx_el+1; i++){
        if (abs(x(i))<Vw_width/2.0)
        {
            a_array(i) = 0.008;
            b_array(i) = 0.012;
            theta_ini(i) = L/V_0*exp((a_array(i)*log(2*sinh(tau_ini/(a_array(i)*sigma_ini)))-f_0-a_array(i)*log(V_ini/V_0))/b_array(i));
        }
        else
        {
            b_array(i) = 0.08;
            a_array(i) = 0.12;
            theta_ini(i) = L/V_0*exp((a_array(i)*log(2*sinh(tau_ini/(a_array(i)*sigma_ini)))-f_0-a_array(i)*log(V_ini/V_0))/b_array(i));
        }
    }

    theta_n = theta_ini;
    MatrixXd d_tau = MatrixXd::Zero(nx_el+1,numt);
    for (int i=0;i<numt;i++)
    {
        d_tau.col(i)=75e6*VectorXd::Ones(nx_el+1,1)+d_tau_0*F_r*(G_t(i));
       // d_tau.col(i)=75e6*VectorXd::Ones(nx_el+1,1)+3.75e6*F_r+dt*0.3125e7*i*VectorXd::Ones(nx_el+1,1);
    }
    // Get the index degree of freedome for each element
    VectorXi index_el = VectorXi::Zero(Ndofn*Nnel,1);
    MatrixXi index_store = MatrixXi::Zero(Nnel*Ndofn,n_el);
    for (int i=0;i<n_el;i++)
    {
        bcdof(Element.row(i),dim,index_el);
        index_store.col(i) = index_el;
    }
    VectorXi BIE_top_surf_index = VectorXi::Zero(Ndofn*(BIE_top_surf_nodes.size()),1);
    VectorXi BIE_bot_surf_index = VectorXi::Zero(Ndofn*(BIE_bot_surf_nodes.size()),1);

    std::vector<Eigen::ArrayXi> Fault_surf_index(num_faults*2);
    for (int i=0; i <num_faults*2; i++)
    {
        Fault_surf_index[i] = ArrayXi::Zero(Ndofn*(fault_nodes[i].size()),1);
    }

    // Getting the faults DOF index
    for (int i=0; i<num_faults*2;i++)
    {
        bcdof_ptr(fault_nodes[i], dim, Fault_surf_index[i].data());
    }
    bcdof(BIE_top_surf_nodes,dim,BIE_top_surf_index);
    bcdof(BIE_bot_surf_nodes,dim,BIE_bot_surf_index);

    // Rate and state initlaized v_n delt_v_n
    for (int i=0;i<nx_el+1;i++)
    {
        delt_v_n(2*i)= V_ini;
  //      v_n(top_surf_index(2*i))= V_ini/2.0;

  //      v_n(bot_surf_index(2*i))= -V_ini/2.0;

        v_n(Fault_surf_index[0](2*i))= V_ini/2.0;

        v_n(Fault_surf_index[1](2*i))= -V_ini/2.0;
    }

    // Calculating the Global Mass Vector (lumped mass)
    // Element mass
    double M=density*dx*dy*1.0;
    VectorXd M_el_vec = M/4*VectorXd::Ones(Nnel*Ndofn,1);
    VectorXd M_global_vec=VectorXd::Zero(n_nodes*Ndofn,1);
    for (int i=0 ; i<n_el;i++)
    {
        index_el = index_store.col(i);
        mapglobal(index_el,M_global_vec,M_el_vec);
    }
    // Element matrix
    MatrixXd ke = MatrixXd::Zero(8,8);
    MatrixXd coord = MatrixXd::Zero(4,2);
    VectorXi Element_0= Element.row(0);
    coord.row(0) = Node.row(Element_0(0));
    coord.row(1) = Node.row(Element_0(1));
    coord.row(2) = Node.row(Element_0(2));
    coord.row(3) = Node.row(Element_0(3));

    std::cout<<coord<<std::endl;

    cal_ke (coord,E,nu,ke);
    // BIE part initiation
    // Setting up the material property for the BIE code
    Material BIE_top_mat = Material(E,nu,density);
    Material BIE_bot_mat = Material(E,nu,density);
    double length = x_max-x_min;
    // infinte bc BIE call infinite_boundary.cc
    PrecomputedKernel h11("kernels/nu_.25_h11.dat");
    PrecomputedKernel h12("kernels/nu_.25_k12.dat");
    PrecomputedKernel h22("kernels/nu_.25_h22.dat");
    InfiniteBoundary BIE_inf_top(length,nx_el+1,1.0,&BIE_top_mat,&h11,&h12,&h22);
    InfiniteBoundary BIE_inf_bot(length,nx_el+1,-1.0,&BIE_bot_mat,&h11,&h12,&h22);
    // BIE setting time step
    BIE_inf_top.setTimeStep(dt);
    BIE_inf_bot.setTimeStep(dt);
    // BIE initialization
    BIE_inf_top.init();
    BIE_inf_bot.init();

    //BIE_initiation(E, nu, density, x_max, x_min, nx_el, dt);
    printf("ready to start\n");
    // Output
    ofstream file;
    file.open("results/num_nodes_fault.bin",ios::binary);
    file.write((char*)(nx_faults.data()),nx_faults.size()*sizeof(int));
    file.close();
    //
    file.open("results/u_n.bin",ios::binary);
    file.close();
    file.open("results/v_n.bin",ios::binary);
    file.close();
    file.open("results/eq_ep_n.bin",ios::binary);
    file.close();

    for (int i=0;i<num_faults;i++)
    {
        std::string slip = "results/slip_"+std::to_string(i)+".bin";
        file.open(slip);
        file.close();
        std::string slip_rate = "results/slip_rate_"+std::to_string(i)+".bin";
        file.open(slip_rate);
        file.close();
        std::string shear = "results/shear_"+std::to_string(i)+".bin";
        file.open(shear);
        file.close();
    }

    // Main time loop
    for (int j=0;j<numt;j++)
    {
        // Compute the global internal force
        VectorXd fe_global= VectorXd::Zero(n_nodes*Ndofn,1);
        cal_fe_global_const_ke(n_nodes, n_el, index_store, q, u_n, v_n, Ndofn, ke, fe_global);
        // Friction subroutine
        VectorXd F_fault = VectorXd::Zero(Ndofn*(nx_el+1),1);
      //  Slip_Weakening(M_global_vec, top_surf_index, bot_surf_index, fe_global, dt, dx, dy, nx_el, delt_v_n, delt_u_n, T_0, tau_s, mu_s, mu_d, Dc, Ndofn, M, F_fault, T_c);
        for (int i=0;i<nx_el+1;i++)
        {
            T_0(2*i) = d_tau(i,j);
        }
        Rate_and_State_Aging(M_global_vec, Fault_surf_index[0], Fault_surf_index[1], fe_global, dt, dx, dy, nx_el, delt_u_n, delt_v_n, T_0, tau_s, a_array, b_array , L, V_0, f_0, Ndofn, M, F_fault, T_c, theta_n, theta_dot_n);

        // Calculate the global force vector
        // Adding contribution of the fault force to the global force vector F_total
        VectorXd F_total = F_ext_global-fe_global;
        mapglobal(Fault_surf_index[0],F_total,-F_fault);
        mapglobal(Fault_surf_index[1],F_total,F_fault);
        // Central Difference Time integration
        time_advance(u_n, v_n, F_total, M_global_vec, dt);

//        for (int i=0 ; i<left_diag_index.size();i++)
//        {
//            u_n(left_diag_index(i)) = 0.0;
//            v_n(left_diag_index(i)) = 0.0;
//            u_n(right_diag_index(i)) = 0.0;
//            v_n(right_diag_index(i)) = 0.0;
//        }
//

        // Get the slip and slip rate
        //cal_slip_slip_rate(u_n, v_n, top_surf_index, bot_surf_index, Ndofn, nx_el, delt_u_n, delt_v_n);
        update_disp_velocity(u_n, v_n, Fault_surf_index[0], Fault_surf_index[1], Ndofn, nx_el, delt_u_n, delt_v_n);

        // Correct the BIE surf nodes solutions from FEM with the BIE solution
        BIE_correct(BIE_top_surf_index, BIE_bot_surf_index, fe_global, Ndofn, nx_el, dx, BIE_inf_top, BIE_inf_bot, u_n, v_n);

        // Set the diagonal to be fixed

        //ofstream file;
        for (int i=0;i<1;i++)
        {
            std::string slip = "results/slip_"+std::to_string(i)+".bin";
            file.open(slip,ios::binary | ios::app);
            file.write((char*)(delt_u_n.data()),delt_u_n.size()*sizeof(double));
            file.close();

            std::string slip_rate = "results/slip_rate_"+std::to_string(i)+".bin";
            file.open(slip_rate,ios::binary | ios::app);
            file.write((char*)(delt_v_n.data()),delt_v_n.size()*sizeof(double));
            file.close();

            std::string shear = "results/shear_"+std::to_string(i)+".bin";
            file.open(shear,ios::binary | ios::app);
            file.write((char*)(T_c.data()),T_c.size()*sizeof(double));
            file.close();
        }

        if (j%100==1)
        {
            file.open("results/u_n.bin",ios::binary | ios::app);
            file.write((char*)(u_n.data()),u_n.size()*sizeof(double));
            file.close();
            file.open("results/v_n.bin",ios::binary | ios::app);
            file.write((char*)(v_n.data()),v_n.size()*sizeof(double));
            file.close();
        }

        printf("Simulation time = %f\n",time(j));

    }
    return 0;
}


/*
 [auto_generated]
 boost/numeric/odeint/stepper/adams_bashforth.hpp

 [begin_description]
 Implementaton of the Adam-Bashforth method a multistep method used for the predictor step in the
 Adams-Bashforth-Moulton method.
 [end_description]

 Copyright 2009-2011 Karsten Ahnert
 Copyright 2009-2011 Mario Mulansky

 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_NUMERIC_ODEINT_STEPPER_ADAMS_BASHFORTH_HPP_INCLUDED
#define BOOST_NUMERIC_ODEINT_STEPPER_ADAMS_BASHFORTH_HPP_INCLUDED

#include <boost/static_assert.hpp>

#include <boost/numeric/odeint/util/bind.hpp>
#include <boost/numeric/odeint/util/unwrap_reference.hpp>

#include <boost/numeric/odeint/algebra/range_algebra.hpp>
#include <boost/numeric/odeint/algebra/default_operations.hpp>

#include <boost/numeric/odeint/util/state_wrapper.hpp>
#include <boost/numeric/odeint/util/is_resizeable.hpp>
#include <boost/numeric/odeint/util/resizer.hpp>

#include <boost/numeric/odeint/stepper/stepper_categories.hpp>
#include <boost/numeric/odeint/stepper/runge_kutta4.hpp>

#include <boost/numeric/odeint/stepper/base/algebra_stepper_base.hpp>

#include <boost/numeric/odeint/stepper/detail/adams_bashforth_coefficients.hpp>
#include <boost/numeric/odeint/stepper/detail/adams_bashforth_call_algebra.hpp>
#include <boost/numeric/odeint/stepper/detail/rotating_buffer.hpp>

namespace boost {
namespace numeric {
namespace odeint {

template<
size_t Steps ,
class State ,
class Value = double ,
class Deriv = State ,
class Time = Value ,
class Algebra = range_algebra ,
class Operations = default_operations ,
class Resizer = initially_resizer ,
class InitializingStepper = runge_kutta4< State , Value , Deriv , Time , Algebra , Operations, Resizer >
>
class adams_bashforth : public algebra_stepper_base< Algebra , Operations >
{

#ifndef DOXYGEN_SKIP
    BOOST_STATIC_ASSERT(( Steps > 0 ));
    BOOST_STATIC_ASSERT(( Steps < 9 ));
#endif

public :

    typedef State state_type;
    typedef state_wrapper< state_type > wrapped_state_type;
    typedef Value value_type;
    typedef Deriv deriv_type;
    typedef state_wrapper< deriv_type > wrapped_deriv_type;
    typedef Time time_type;
    typedef Resizer resizer_type;
    typedef stepper_tag stepper_category;

    typedef InitializingStepper initializing_stepper_type;

    typedef typename algebra_stepper_base< Algebra , Operations >::algebra_type algebra_type;
    typedef typename algebra_stepper_base< Algebra , Operations >::operations_type operations_type;
#ifndef DOXYGEN_SKIP
    typedef adams_bashforth< Steps , State , Value , Deriv , Time , Algebra , Operations , Resizer , InitializingStepper > stepper_type;
#endif
    static const size_t steps = Steps;

    typedef unsigned short order_type;
    static const order_type order_value = steps;

    typedef detail::rotating_buffer< wrapped_deriv_type , steps > step_storage_type;

    order_type order( void ) const { return order_value; }

    adams_bashforth( const algebra_type &algebra = algebra_type() )
    : m_step_storage() , m_resizer() , m_coefficients() ,
      m_steps_initialized( 0 ) , m_initializing_stepper() ,
      m_algebra( algebra )
    { }

    adams_bashforth( const adams_bashforth &stepper )
    : m_step_storage( stepper.m_step_storage ) , m_resizer( stepper.m_resizer ) , m_coefficients() ,
      m_steps_initialized( stepper.m_steps_initialized ) , m_initializing_stepper( stepper.m_initializing_stepper ) ,
      m_algebra( stepper.m_algebra )
    { }

    adams_bashforth& operator=( const adams_bashforth &stepper )
    {
        m_resizer = stepper.m_resizer;
        m_step_storage = stepper.m_step_storage;
        m_algebra = stepper.m_algebra;
        return *this;
    }

    /*
     * Version 1 : do_step( system , x , t , dt );
     *
     * solves the forwarding problem
     */
    template< class System , class StateInOut >
    void do_step( System system , StateInOut &x , time_type t , time_type dt )
    {
        do_step( system , x , t , x , dt );
    }

    /**
     * \brief Second version to solve the forwarding problem, can be called with Boost.Range as StateInOut.
     */
    template< class System , class StateInOut >
    void do_step( System system , const StateInOut &x , time_type t , time_type dt )
    {
        do_step( system , x , t , x , dt );
    }

    /*
     * Version 2 : do_step( system , in , t , out , dt );
     *
     * solves the forwarding problem
     */

    template< class System , class StateIn , class StateOut >
    void do_step( System system , const StateIn &in , time_type t , StateOut &out , time_type dt )
    {
        do_step_impl( system , in , t , out , dt );
    }

    /**
     * \brief Second version to solve the forwarding problem, can be called with Boost.Range as StateOut.
     */
    template< class System , class StateIn , class StateOut >
    void do_step( System system , const StateIn &in , time_type t , const StateOut &out , time_type dt )
    {
        do_step_impl( system , in , t , out , dt );
    }

    template< class StateType >
    void adjust_size( const StateType &x )
    {
        resize_impl( x );
    }

    const step_storage_type& step_storage( void ) const
    {
        return m_step_storage;
    }

    step_storage_type& step_storage( void )
    {
        return m_step_storage;
    }

    template< class ExplicitStepper , class System , class StateIn >
    void initialize( ExplicitStepper explicit_stepper , System system , StateIn &x , time_type &t , time_type dt )
    {
        typename odeint::unwrap_reference< ExplicitStepper >::type &stepper = explicit_stepper;
        typename odeint::unwrap_reference< System >::type &sys = system;

        m_resizer.adjust_size( x , detail::bind( &stepper_type::template resize_impl<StateIn> , detail::ref( *this ) , detail::_1 ) );

        for( size_t i=0 ; i<steps-1 ; ++i )
        {
            if( i != 0 ) m_step_storage.rotate();
            sys( x , m_step_storage[0].m_v , t );
            stepper.do_step( system , x , m_step_storage[0].m_v , t , dt );
            t += dt;
        }
        m_steps_initialized = steps;
    }

    template< class System , class StateIn >
    void initialize( System system , StateIn &x , time_type &t , time_type dt )
    {
        initialize( detail::ref( m_initializing_stepper ) , system , x , t , dt );
    }

    void reset( void )
    {
        m_steps_initialized = 0;
    }

    bool is_initialized( void ) const
    {
        return m_steps_initialized >= steps;
    }

    const initializing_stepper_type& initializing_stepper( void ) const { return m_initializing_stepper; }

    initializing_stepper_type& initializing_stepper( void ) { return m_initializing_stepper; }

private:

    template< class System , class StateIn , class StateOut >
    void do_step_impl( System system , const StateIn &in , time_type t , StateOut &out , time_type dt )
    {
        typename odeint::unwrap_reference< System >::type &sys = system;
        if( m_resizer.adjust_size( in , detail::bind( &stepper_type::template resize_impl<StateIn> , detail::ref( *this ) , detail::_1 ) ) )
        {
            m_steps_initialized = 0;
        }

        if( m_steps_initialized < steps - 1 )
        {
            if( m_steps_initialized != 0 ) m_step_storage.rotate();
            sys( in , m_step_storage[0].m_v , t );
            m_initializing_stepper.do_step( system , in , m_step_storage[0].m_v , t , out , dt );
            m_steps_initialized++;
        }
        else
        {
            m_step_storage.rotate();
            sys( in , m_step_storage[0].m_v , t );
            detail::adams_bashforth_call_algebra< steps , algebra_type , operations_type >()( m_algebra , in , out , m_step_storage , m_coefficients , dt );
        }
    }

    template< class StateIn >
    bool resize_impl( const StateIn &x )
    {
        bool resized( false );
        for( size_t i=0 ; i<steps ; ++i )
        {
            resized |= adjust_size_by_resizeability( m_step_storage[i] , x , typename is_resizeable<deriv_type>::type() );
        }
        return resized;
    }

    step_storage_type m_step_storage;
    resizer_type m_resizer;
    const detail::adams_bashforth_coefficients< value_type , steps > m_coefficients;
    size_t m_steps_initialized;
    initializing_stepper_type m_initializing_stepper;

protected:

    algebra_type m_algebra;
};

/***** DOXYGEN *****/

/**
 * \class adams_bashforth
 * \brief The Adams-Bashforth multistep algorithm.
 *
 * The Adams-Bashforth method is a multi-step algorithm with configurable step
 * number. The step number is specified as template parameter Steps and it
 * then uses the result from the previous Steps steps. See also
 * en.wikipedia.org/wiki/Linear_multistep_method.
 * Currently, a maximum of Steps=8 is supported.
 * The method is explicit and fulfills the Stepper concept. Step size control
 * or continuous output are not provided.
 *
 * This class derives from algebra_base and inherits its interface via
 * CRTP (current recurring template pattern). For more details see
 * algebra_stepper_base.
 *
 * \tparam Steps The number of steps (maximal 8).
 * \tparam State The state type.
 * \tparam Value The value type.
 * \tparam Deriv The type representing the time derivative of the state.
 * \tparam Time The time representing the independent variable - the time.
 * \tparam Algebra The algebra type.
 * \tparam Operations The operations type.
 * \tparam Resizer The resizer policy type.
 * \tparam InitializingStepper The stepper for the first two steps.
 */

    /**
     * \fn adams_bashforth::adams_bashforth( const algebra_type &algebra )
     * \brief Constructs the adams_bashforth class. This constructor can be used as a default
     * constructor if the algebra has a default constructor.
     * \param algebra A copy of algebra is made and stored.
     */

    /**
     * \fn order_type adams_bashforth::order( void ) const
     * \brief Returns the order of the algorithm, which is equal to the number of steps.
     * \return order of the method.
     */

    /**
     * \fn void adams_bashforth::do_step( System system , StateInOut &x , time_type t , time_type dt )
     * \brief This method performs one step. It transforms the result in-place.
     *
     * \param system The system function to solve, hence the r.h.s. of the ordinary differential equation. It must fulfill the
     *               Simple System concept.
     * \param x The state of the ODE which should be solved. After calling do_step the result is updated in x.
     * \param t The value of the time, at which the step should be performed.
     * \param dt The step size.
     */

    /**
     * \fn void adams_bashforth::do_step( System system , const StateIn &in , time_type t , StateOut &out , time_type dt )
     * \brief The method performs one step with the stepper passed by Stepper. The state of the ODE is updated out-of-place.
     *
     * \param system The system function to solve, hence the r.h.s. of the ODE. It must fulfill the
     *               Simple System concept.
     * \param in The state of the ODE which should be solved. in is not modified in this method
     * \param t The value of the time, at which the step should be performed.
     * \param out The result of the step is written in out.
     * \param dt The step size.
     */

    /**
     * \fn void adams_bashforth::adjust_size( const StateType &x )
     * \brief Adjust the size of all temporaries in the stepper manually.
     * \param x A state from which the size of the temporaries to be resized is deduced.
     */

    /**
     * \fn const step_storage_type& adams_bashforth::step_storage( void ) const
     * \brief Returns the storage of intermediate results.
     * \return The storage of intermediate results.
     */

    /**
     * \fn step_storage_type& adams_bashforth::step_storage( void )
     * \brief Returns the storage of intermediate results.
     * \return The storage of intermediate results.
     */

    /**
     * \fn void adams_bashforth::initialize( ExplicitStepper explicit_stepper , System system , StateIn &x , time_type &t , time_type dt )
     * \brief Initialized the stepper. Does Steps-1 steps with the explicit_stepper to fill the buffer.
     * \param explicit_stepper the stepper used to fill the buffer of previous step results
     * \param system The system function to solve, hence the r.h.s. of the ordinary differential equation. It must fulfill the
     *               Simple System concept.
     * \param x The state of the ODE which should be solved. After calling do_step the result is updated in x.
     * \param t The value of the time, at which the step should be performed.
     * \param dt The step size.
     */

    /**
     * \fn void adams_bashforth::initialize( System system , StateIn &x , time_type &t , time_type dt )
     * \brief Initialized the stepper. Does Steps-1 steps with an internal instance of InitializingStepper to fill the buffer.
     * \note The state x and time t are updated to the values after Steps-1 initial steps.
     * \param system The system function to solve, hence the r.h.s. of the ordinary differential equation. It must fulfill the
     *               Simple System concept.
     * \param x The initial state of the ODE which should be solved, updated in this method.
     * \param t The initial value of the time, updated in this method.
     * \param dt The step size.
     */

    /**
     * \fn void adams_bashforth::reset( void )
     * \brief Resets the internal buffer of the stepper.
     */

    /**
     * \fn bool adams_bashforth::is_initialized( void ) const
     * \brief Returns true if the stepper has been initialized.
     * \return bool true if stepper is initialized, false otherwise
     */

    /**
     * \fn const initializing_stepper_type& adams_bashforth::initializing_stepper( void ) const
     * \brief Returns the internal initializing stepper instance.
     * \return initializing_stepper
     */

    /**
     * \fn const initializing_stepper_type& adams_bashforth::initializing_stepper( void ) const
     * \brief Returns the internal initializing stepper instance.
     * \return initializing_stepper
     */

    /**
     * \fn initializing_stepper_type& adams_bashforth::initializing_stepper( void )
     * \brief Returns the internal initializing stepper instance.
     * \return initializing_stepper
     */

} // odeint
} // numeric
} // boost

#endif // BOOST_NUMERIC_ODEINT_STEPPER_ADAMS_BASHFORTH_HPP_INCLUDED