/* Author: Wolfgang Bangerth, Texas A&M University, 2006, 2007 */

/*    $Id: step-27.cc 27657 2012-11-21 13:19:08Z bangerth $       */
/*                                                                */
/*    Copyright (C) 2006-2012 by the deal.II authors */
/*                                                                */
/*    This file is subject to QPL and may not be  distributed     */
/*    without copyright and license information. Please refer     */
/*    to the file deal.II/doc/license.html for the  text  and     */
/*    further information on this license.                        */

// @sect3{Include files}

// The first few files have already been covered in previous examples and will
// thus not be further commented on.
#include <deal.II/base/quadrature_lib.h>
#include <deal.II/base/function.h>
#include <deal.II/base/logstream.h>
#include <deal.II/base/utilities.h>
#include <deal.II/lac/vector.h>
#include <deal.II/lac/full_matrix.h>
#include <deal.II/lac/sparse_matrix.h>
#include <deal.II/lac/solver_cg.h>
#include <deal.II/lac/precondition.h>
#include <deal.II/lac/constraint_matrix.h>
#include <deal.II/grid/tria.h>
#include <deal.II/grid/grid_generator.h>
#include <deal.II/grid/tria_accessor.h>
#include <deal.II/grid/tria_iterator.h>
#include <deal.II/grid/grid_refinement.h>
#include <deal.II/dofs/dof_accessor.h>
#include <deal.II/dofs/dof_tools.h>
#include <deal.II/fe/fe_q.h>
#include <deal.II/numerics/vector_tools.h>
#include <deal.II/numerics/matrix_tools.h>
#include <deal.II/numerics/data_out.h>
#include <deal.II/numerics/error_estimator.h>

// These are the new files we need. The first one provides an alternative to
// the usual SparsityPattern class and the CompressedSparsityPattern class
// already discussed in step-11 and step-18. The last two provide hp
// versions of the DoFHandler and FEValues classes as described in the
// introduction of this program.
#include <deal.II/lac/compressed_set_sparsity_pattern.h>
#include <deal.II/hp/dof_handler.h>
#include <deal.II/hp/fe_values.h>

// The last set of include files are standard C++ headers. We need support for
// complex numbers when we compute the Fourier transform.
#include <fstream>
#include <iostream>
#include <complex>

// Finally, this is as in previous programs:
namespace Step27
{
  using namespace dealii;

  // @sect3{The main class}

  // The main class of this program looks very much like the one already used
  // in the first few tutorial programs, for example the one in step-6. The
  // main difference is that we have merged the refine_grid and output_results
  // functions into one since we will also want to output some of the
  // quantities used in deciding how to refine the mesh (in particular the
  // estimated smoothness of the solution). There is also a function that
  // computes this estimated smoothness, as discussed in the introduction.
  //
  // As far as member variables are concerned, we use the same structure as
  // already used in step-6, but instead of a regular DoFHandler we use an
  // object of type hp::DoFHandler, and we need collections instead of
  // individual finite element, quadrature, and face quadrature objects. We
  // will fill these collections in the constructor of the class. The last
  // variable, max_degree, indicates the maximal polynomial
  // degree of shape functions used.
  template <int dim>
  class LaplaceProblem
  {
  public:
    LaplaceProblem ();
    ~LaplaceProblem ();

    void run ();

  private:
    void setup_system ();
    void assemble_system ();
    void solve ();
    void create_coarse_grid ();
    void estimate_smoothness (Vector<float> &smoothness_indicators) const;
    void postprocess (const unsigned int cycle);

    Triangulation<dim>   triangulation;

    hp::DoFHandler<dim>      dof_handler;
    hp::FECollection<dim>    fe_collection;
    hp::QCollection<dim>     quadrature_collection;
    hp::QCollection<dim-1>   face_quadrature_collection;

    ConstraintMatrix     constraints;

    SparsityPattern      sparsity_pattern;
    SparseMatrix<double> system_matrix;

    Vector<double>       solution;
    Vector<double>       system_rhs;

    const unsigned int max_degree;
  };

  // @sect3{Equation data}
  //
  // Next, let us define the right hand side function for this problem. It is
  // $x+1$ in 1d, $(x+1)(y+1)$ in 2d, and so on.
  template <int dim>
  class RightHandSide : public Function<dim>
  {
  public:
    RightHandSide () : Function<dim> () {}

    virtual double value (const Point<dim>   &p,
                          const unsigned int  component) const;
  };

  template <int dim>
  double
  RightHandSide<dim>::value (const Point<dim>   &p,
                             const unsigned int  /*component*/) const
  {
    double product = 1;
    for (unsigned int d=0; d<dim; ++d)
      product *= (p[d]+1);
    return product;
  }

  // @sect3{Implementation of the main class}

  // @sect4{LaplaceProblem::LaplaceProblem}

  // The constructor of this class is fairly straightforward. It associates
  // the hp::DoFHandler object with the triangulation, and then sets the
  // maximal polynomial degree to 7 (in 1d and 2d) or 5 (in 3d and higher). We
  // do so because using higher order polynomial degrees becomes prohibitively
  // expensive, especially in higher space dimensions.
  //
  // Following this, we fill the collections of finite element, and cell and
  // face quadrature objects. We start with quadratic elements, and each
  // quadrature formula is chosen so that it is appropriate for the matching
  // finite element in the hp::FECollection object.
  template <int dim>
  LaplaceProblem<dim>::LaplaceProblem ()
    :
    dof_handler (triangulation),
    max_degree (dim <= 2 ? 7 : 5)
  {
    for (unsigned int degree=2; degree<=max_degree; ++degree)
      {
        fe_collection.push_back (FE_Q<dim>(degree));
        quadrature_collection.push_back (QGauss<dim>(degree+1));
        face_quadrature_collection.push_back (QGauss<dim-1>(degree+1));
      }
  }

  // @sect4{LaplaceProblem::~LaplaceProblem}

  // The destructor is unchanged from what we already did in step-6:
  template <int dim>
  LaplaceProblem<dim>::~LaplaceProblem ()
  {
    dof_handler.clear ();
  }

  // @sect4{LaplaceProblem::setup_system}
  //
  // This function is again an almost verbatim copy of what we already did in
  // step-6. The first change is that we append the Dirichlet boundary
  // conditions to the ConstraintMatrix object, which we consequently call
  // just constraints instead of
  // hanging_node_constraints. The second difference is that we
  // don't directly build the sparsity pattern, but first create an
  // intermediate object that we later copy into the usual SparsityPattern
  // data structure, since this is more efficient for the problem with many
  // entries per row (and different number of entries in different rows). In
  // another slight deviation, we do not first build the sparsity pattern and
  // then condense away constrained degrees of freedom, but pass the
  // constraint matrix object directly to the function that builds the
  // sparsity pattern. We disable the insertion of constrained entries with
  // <tt>false</tt> as fourth argument in the DoFTools::make_sparsity_pattern
  // function. All of these changes are explained in the introduction of this
  // program.
  //
  // The last change, maybe hidden in plain sight, is that the dof_handler
  // variable here is an hp object -- nevertheless all the function calls we
  // had before still work in exactly the same way as they always did.
  template <int dim>
  void LaplaceProblem<dim>::setup_system ()
  {
    dof_handler.distribute_dofs (fe_collection);

    solution.reinit (dof_handler.n_dofs());
    system_rhs.reinit (dof_handler.n_dofs());

    constraints.clear ();
    DoFTools::make_hanging_node_constraints (dof_handler,
                                             constraints);
    VectorTools::interpolate_boundary_values (dof_handler,
                                              0,
                                              ZeroFunction<dim>(),
                                              constraints);
    constraints.close ();

    CompressedSetSparsityPattern csp (dof_handler.n_dofs(),
                                      dof_handler.n_dofs());
    DoFTools::make_sparsity_pattern (dof_handler, csp, constraints, false);
    sparsity_pattern.copy_from (csp);

    system_matrix.reinit (sparsity_pattern);
  }

  // @sect4{LaplaceProblem::assemble_system}

  // This is the function that assembles the global matrix and right hand side
  // vector from the local contributions of each cell. Its main working is as
  // has been described in many of the tutorial programs before. The
  // significant deviations are the ones necessary for hp finite
  // element methods. In particular, that we need to use a collection of
  // FEValues object (implemented through the hp::FEValues class), and that we
  // have to eliminate constrained degrees of freedom already when copying
  // local contributions into global objects. Both of these are explained in
  // detail in the introduction of this program.
  //
  // One other slight complication is the fact that because we use different
  // polynomial degrees on different cells, the matrices and vectors holding
  // local contributions do not have the same size on all cells. At the
  // beginning of the loop over all cells, we therefore each time have to
  // resize them to the correct size (given by
  // dofs_per_cell). Because these classes are implement in such
  // a way that reducing the size of a matrix or vector does not release the
  // currently allocated memory (unless the new size is zero), the process of
  // resizing at the beginning of the loop will only require re-allocation of
  // memory during the first few iterations. Once we have found in a cell with
  // the maximal finite element degree, no more re-allocations will happen
  // because all subsequent reinit calls will only set the size
  // to something that fits the currently allocated memory. This is important
  // since allocating memory is expensive, and doing so every time we visit a
  // new cell would take significant compute time.
  template <int dim>
  void LaplaceProblem<dim>::assemble_system ()
  {
    hp::FEValues<dim> hp_fe_values (fe_collection,
                                    quadrature_collection,
                                    update_values    |  update_gradients |
                                    update_quadrature_points  |  update_JxW_values);

    const RightHandSide<dim> rhs_function;

    FullMatrix<double>   cell_matrix;
    Vector<double>       cell_rhs;

    std::vector<unsigned int> local_dof_indices;

    typename hp::DoFHandler<dim>::active_cell_iterator
    cell = dof_handler.begin_active(),
    endc = dof_handler.end();
    for (; cell!=endc; ++cell)
      {
        const unsigned int   dofs_per_cell = cell->get_fe().dofs_per_cell;

        cell_matrix.reinit (dofs_per_cell, dofs_per_cell);
        cell_matrix = 0;

        cell_rhs.reinit (dofs_per_cell);
        cell_rhs = 0;

        hp_fe_values.reinit (cell);

        const FEValues<dim> &fe_values = hp_fe_values.get_present_fe_values ();

        std::vector<double>  rhs_values (fe_values.n_quadrature_points);
        rhs_function.value_list (fe_values.get_quadrature_points(),
                                 rhs_values);

        for (unsigned int q_point=0;
             q_point<fe_values.n_quadrature_points;
             ++q_point)
          for (unsigned int i=0; i<dofs_per_cell; ++i)
            {
              for (unsigned int j=0; j<dofs_per_cell; ++j)
                cell_matrix(i,j) += (fe_values.shape_grad(i,q_point) *
                                     fe_values.shape_grad(j,q_point) *
                                     fe_values.JxW(q_point));

              cell_rhs(i) += (fe_values.shape_value(i,q_point) *
                              rhs_values[q_point] *
                              fe_values.JxW(q_point));
            }

        local_dof_indices.resize (dofs_per_cell);
        cell->get_dof_indices (local_dof_indices);

        constraints.distribute_local_to_global (cell_matrix, cell_rhs,
                                                local_dof_indices,
                                                system_matrix, system_rhs);
      }

    // Now with the loop over all cells finished, we are done for this
    // function. The steps we still had to do at this point in earlier
    // tutorial programs, namely condensing hanging node constraints and
    // applying Dirichlet boundary conditions, have been taken care of by the
    // ConstraintMatrix object constraints on the fly.
  }

  // @sect4{LaplaceProblem::solve}

  // The function solving the linear system is entirely unchanged from
  // previous examples. We simply try to reduce the initial residual (which
  // equals the $l_2$ norm of the right hand side) by a certain factor:
  template <int dim>
  void LaplaceProblem<dim>::solve ()
  {
    SolverControl           solver_control (system_rhs.size(),
                                            1e-8*system_rhs.l2_norm());
    SolverCG<>              cg (solver_control);

    PreconditionSSOR<> preconditioner;
    preconditioner.initialize(system_matrix, 1.2);

    cg.solve (system_matrix, solution, system_rhs,
              preconditioner);

    constraints.distribute (solution);
  }

  // @sect4{LaplaceProblem::postprocess}

  // After solving the linear system, we will want to postprocess the
  // solution. Here, all we do is to estimate the error, estimate the local
  // smoothness of the solution as described in the introduction, then write
  // graphical output, and finally refine the mesh in both $h$ and $p$
  // according to the indicators computed before. We do all this in the same
  // function because we want the estimated error and smoothness indicators
  // not only for refinement, but also include them in the graphical output.
  template <int dim>
  void LaplaceProblem<dim>::postprocess (const unsigned int cycle)
  {
    // Let us start with computing estimated error and smoothness indicators,
    // which each are one number for each active cell of our
    // triangulation. For the error indicator, we use the KellyErrorEstimator
    // class as always. Estimating the smoothness is done in the respective
    // function of this class; that function is discussed further down below:
    Vector<float> estimated_error_per_cell (triangulation.n_active_cells());
    KellyErrorEstimator<dim>::estimate (dof_handler,
                                        face_quadrature_collection,
                                        typename FunctionMap<dim>::type(),
                                        solution,
                                        estimated_error_per_cell);

    Vector<float> smoothness_indicators (triangulation.n_active_cells());
    estimate_smoothness (smoothness_indicators);

    // Next we want to generate graphical output. In addition to the two
    // estimated quantities derived above, we would also like to output the
    // polynomial degree of the finite elements used on each of the elements
    // on the mesh.
    //
    // The way to do that requires that we loop over all cells and poll the
    // active finite element index of them using
    // cell-@>active_fe_index(). We then use the result of this
    // operation and query the finite element collection for the finite
    // element with that index, and finally determine the polynomial degree of
    // that element. The result we put into a vector with one element per
    // cell. The DataOut class requires this to be a vector of
    // float or double, even though our values are
    // all integers, so that it what we use:
    {
      Vector<float> fe_degrees (triangulation.n_active_cells());
      {
        typename hp::DoFHandler<dim>::active_cell_iterator
        cell = dof_handler.begin_active(),
        endc = dof_handler.end();
        for (unsigned int index=0; cell!=endc; ++cell, ++index)
          fe_degrees(index)
            = fe_collection[cell->active_fe_index()].degree;
      }

      // With now all data vectors available -- solution, estimated errors and
      // smoothness indicators, and finite element degrees --, we create a
      // DataOut object for graphical output and attach all data. Note that
      // the DataOut class has a second template argument (which defaults to
      // DoFHandler@<dim@>, which is why we have never seen it in previous
      // tutorial programs) that indicates the type of DoF handler to be
      // used. Here, we have to use the hp::DoFHandler class:
      DataOut<dim,hp::DoFHandler<dim> > data_out;

      data_out.attach_dof_handler (dof_handler);
      data_out.add_data_vector (solution, "solution");
      data_out.add_data_vector (estimated_error_per_cell, "error");
      data_out.add_data_vector (smoothness_indicators, "smoothness");
      data_out.add_data_vector (fe_degrees, "fe_degree");
      data_out.build_patches ();

      // The final step in generating output is to determine a file name, open
      // the file, and write the data into it (here, we use VTK format):
      const std::string filename = "solution-" +
                                   Utilities::int_to_string (cycle, 2) +
                                   ".vtk";
      std::ofstream output (filename.c_str());
      data_out.write_vtk (output);
    }

    // After this, we would like to actually refine the mesh, in both $h$ and
    // $p$. The way we are going to do this is as follows: first, we use the
    // estimated error to flag those cells for refinement that have the
    // largest error. This is what we have always done:
    {
      GridRefinement::refine_and_coarsen_fixed_number (triangulation,
                                                       estimated_error_per_cell,
                                                       0.3, 0.03);

      // Next we would like to figure out which of the cells that have been
      // flagged for refinement should actually have $p$ increased instead of
      // $h$ decreased. The strategy we choose here is that we look at the
      // smoothness indicators of those cells that are flagged for refinement,
      // and increase $p$ for those with a smoothness larger than a certain
      // threshold. For this, we first have to determine the maximal and
      // minimal values of the smoothness indicators of all flagged cells,
      // which we do using a loop over all cells and comparing current minimal
      // and maximal values. (We start with the minimal and maximal values of
      // all cells, a range within which the minimal and maximal values
      // on cells flagged for refinement must surely lie.) Absent any better
      // strategies, we will then set the threshold above which will increase
      // $p$ instead of reducing $h$ as the mean value between minimal and
      // maximal smoothness indicators on cells flagged for refinement:
      float max_smoothness = *std::min_element (smoothness_indicators.begin(),
                                                smoothness_indicators.end()),
                             min_smoothness = *std::max_element (smoothness_indicators.begin(),
                                                                 smoothness_indicators.end());
      {
        typename hp::DoFHandler<dim>::active_cell_iterator
        cell = dof_handler.begin_active(),
        endc = dof_handler.end();
        for (unsigned int index=0; cell!=endc; ++cell, ++index)
          if (cell->refine_flag_set())
            {
              max_smoothness = std::max (max_smoothness,
                                         smoothness_indicators(index));
              min_smoothness = std::min (min_smoothness,
                                         smoothness_indicators(index));
            }
      }
      const float threshold_smoothness = (max_smoothness + min_smoothness) / 2;

      // With this, we can go back, loop over all cells again, and for those
      // cells for which (i) the refinement flag is set, (ii) the smoothness
      // indicator is larger than the threshold, and (iii) we still have a
      // finite element with a polynomial degree higher than the current one
      // in the finite element collection, we then increase the polynomial
      // degree and in return remove the flag indicating that the cell should
      // undergo bisection. For all other cells, the refinement flags remain
      // untouched:
      {
        typename hp::DoFHandler<dim>::active_cell_iterator
        cell = dof_handler.begin_active(),
        endc = dof_handler.end();
        for (unsigned int index=0; cell!=endc; ++cell, ++index)
          if (cell->refine_flag_set()
              &&
              (smoothness_indicators(index) > threshold_smoothness)
              &&
              (cell->active_fe_index()+1 < fe_collection.size()))
            {
              cell->clear_refine_flag();
              cell->set_active_fe_index (cell->active_fe_index() + 1);
            }
      }

      // At the end of this procedure, we then refine the mesh. During this
      // process, children of cells undergoing bisection inherit their mother
      // cell's finite element index:
      triangulation.execute_coarsening_and_refinement ();
    }
  }

  // @sect4{LaplaceProblem::create_coarse_grid}

  // The following function is used when creating the initial grid. It is a
  // specialization for the 2d case, i.e. a corresponding function needs to be
  // implemented if the program is run in anything other then 2d. The function
  // is actually stolen from step-14 and generates the same mesh used already
  // there, i.e. the square domain with the square hole in the middle. The
  // meaning of the different parts of this function are explained in the
  // documentation of step-14:
  template <>
  void LaplaceProblem<2>::create_coarse_grid ()
  {
    const unsigned int dim = 2;

    static const Point<2> vertices_1[]
      = {  Point<2> (-1.,   -1.),
           Point<2> (-1./2, -1.),
           Point<2> (0.,    -1.),
           Point<2> (+1./2, -1.),
           Point<2> (+1,    -1.),

           Point<2> (-1.,   -1./2.),
           Point<2> (-1./2, -1./2.),
           Point<2> (0.,    -1./2.),
           Point<2> (+1./2, -1./2.),
           Point<2> (+1,    -1./2.),

           Point<2> (-1.,   0.),
           Point<2> (-1./2, 0.),
           Point<2> (+1./2, 0.),
           Point<2> (+1,    0.),

           Point<2> (-1.,   1./2.),
           Point<2> (-1./2, 1./2.),
           Point<2> (0.,    1./2.),
           Point<2> (+1./2, 1./2.),
           Point<2> (+1,    1./2.),

           Point<2> (-1.,   1.),
           Point<2> (-1./2, 1.),
           Point<2> (0.,    1.),
           Point<2> (+1./2, 1.),
           Point<2> (+1,    1.)
        };
    const unsigned int
    n_vertices = sizeof(vertices_1) / sizeof(vertices_1[0]);
    const std::vector<Point<dim> > vertices (&vertices_1[0],
                                             &vertices_1[n_vertices]);
    static const int cell_vertices[][GeometryInfo<dim>::vertices_per_cell]
    = {{0, 1, 5, 6},
      {1, 2, 6, 7},
      {2, 3, 7, 8},
      {3, 4, 8, 9},
      {5, 6, 10, 11},
      {8, 9, 12, 13},
      {10, 11, 14, 15},
      {12, 13, 17, 18},
      {14, 15, 19, 20},
      {15, 16, 20, 21},
      {16, 17, 21, 22},
      {17, 18, 22, 23}
    };
    const unsigned int
    n_cells = sizeof(cell_vertices) / sizeof(cell_vertices[0]);

    std::vector<CellData<dim> > cells (n_cells, CellData<dim>());
    for (unsigned int i=0; i<n_cells; ++i)
      {
        for (unsigned int j=0;
             j<GeometryInfo<dim>::vertices_per_cell;
             ++j)
          cells[i].vertices[j] = cell_vertices[i][j];
        cells[i].material_id = 0;
      }

    triangulation.create_triangulation (vertices,
                                        cells,
                                        SubCellData());
    triangulation.refine_global (3);
  }

  // @sect4{LaplaceProblem::run}

  // This function implements the logic of the program, as did the respective
  // function in most of the previous programs already, see for example
  // step-6.
  //
  // Basically, it contains the adaptive loop: in the first iteration create a
  // coarse grid, and then set up the linear system, assemble it, solve, and
  // postprocess the solution including mesh refinement. Then start over
  // again. In the meantime, also output some information for those staring at
  // the screen trying to figure out what the program does:
  template <int dim>
  void LaplaceProblem<dim>::run ()
  {
    for (unsigned int cycle=0; cycle<6; ++cycle)
      {
        std::cout << "Cycle " << cycle << ':' << std::endl;

        if (cycle == 0)
          create_coarse_grid ();

        setup_system ();

        std::cout << "   Number of active cells:       "
                  << triangulation.n_active_cells()
                  << std::endl
                  << "   Number of degrees of freedom: "
                  << dof_handler.n_dofs()
                  << std::endl
                  << "   Number of constraints       : "
                  << constraints.n_constraints()
                  << std::endl;

        assemble_system ();
        solve ();
        postprocess (cycle);
      }
  }

  // @sect4{LaplaceProblem::estimate_smoothness}

  // This last function of significance implements the algorithm to estimate
  // the smoothness exponent using the algorithms explained in detail in the
  // introduction. We will therefore only comment on those points that are of
  // implementational importance.
  template <int dim>
  void
  LaplaceProblem<dim>::
  estimate_smoothness (Vector<float> &smoothness_indicators) const
  {
    // The first thing we need to do is to define the Fourier vectors ${\bf
    // k}$ for which we want to compute Fourier coefficients of the solution
    // on each cell. In 2d, we pick those vectors ${\bf k}=(\pi i, \pi j)^T$
    // for which $\sqrt{i^2+j^2}\le N$, with $i,j$ integers and $N$ being the
    // maximal polynomial degree we use for the finite elements in this
    // program. The 3d case is handled analogously. 1d and dimensions higher
    // than 3 are not implemented, and we guard our implementation by making
    // sure that we receive an exception in case someone tries to compile the
    // program for any of these dimensions.
    //
    // We exclude ${\bf k}=0$ to avoid problems computing $|{\bf k}|^{-mu}$
    // and $\ln |{\bf k}|$. The other vectors are stored in the field
    // k_vectors. In addition, we store the square of the
    // magnitude of each of these vectors (up to a factor $\pi^2$) in the
    // k_vectors_magnitude array -- we will need that when we
    // attempt to find out which of those Fourier coefficients corresponding
    // to Fourier vectors of the same magnitude is the largest:
    const unsigned int N = max_degree;

    std::vector<Tensor<1,dim> > k_vectors;
    std::vector<unsigned int>   k_vectors_magnitude;
    switch (dim)
      {
      case 2:
      {
        for (unsigned int i=0; i<N; ++i)
          for (unsigned int j=0; j<N; ++j)
            if (!((i==0) && (j==0))
                &&
                (i*i + j*j < N*N))
              {
                k_vectors.push_back (Point<dim>(numbers::PI * i,
                                                numbers::PI * j));
                k_vectors_magnitude.push_back (i*i+j*j);
              }

        break;
      }

      case 3:
      {
        for (unsigned int i=0; i<N; ++i)
          for (unsigned int j=0; j<N; ++j)
            for (unsigned int k=0; k<N; ++k)
              if (!((i==0) && (j==0) && (k==0))
                  &&
                  (i*i + j*j + k*k < N*N))
                {
                  k_vectors.push_back (Point<dim>(numbers::PI * i,
                                                  numbers::PI * j,
                                                  numbers::PI * k));
                  k_vectors_magnitude.push_back (i*i+j*j+k*k);
                }

        break;
      }

      default:
        Assert (false, ExcNotImplemented());
      }

    // After we have set up the Fourier vectors, we also store their total
    // number for simplicity, and compute the logarithm of the magnitude of
    // each of these vectors since we will need it many times over further
    // down below:
    const unsigned n_fourier_modes = k_vectors.size();
    std::vector<double> ln_k (n_fourier_modes);
    for (unsigned int i=0; i<n_fourier_modes; ++i)
      ln_k[i] = std::log (k_vectors[i].norm());

    // Next, we need to assemble the matrices that do the Fourier transforms
    // for each of the finite elements we deal with, i.e. the matrices ${\cal
    // F}_{{\bf k},j}$ defined in the introduction. We have to do that for
    // each of the finite elements in use. Note that these matrices are
    // complex-valued, so we can't use the FullMatrix class. Instead, we use
    // the Table class template.
    std::vector<Table<2,std::complex<double> > >
    fourier_transform_matrices (fe_collection.size());

    // In order to compute them, we of course can't perform the Fourier
    // transform analytically, but have to approximate it using quadrature. To
    // this end, we use a quadrature formula that is obtained by iterating a
    // 2-point Gauss formula as many times as the maximal exponent we use for
    // the term $e^{i{\bf k}\cdot{\bf x}}$:
    QGauss<1>      base_quadrature (2);
    QIterated<dim> quadrature (base_quadrature, N);

    // With this, we then loop over all finite elements in use, reinitialize
    // the respective matrix ${\cal F}$ to the right size, and integrate each
    // entry of the matrix numerically as ${\cal F}_{{\bf k},j}=\sum_q
    // e^{i{\bf k}\cdot {\bf x}}\varphi_j({\bf x}_q) w_q$, where $x_q$ are the
    // quadrature points and $w_q$ are the quadrature weights. Note that the
    // imaginary unit $i=\sqrt{-1}$ is obtained from the standard C++ classes
    // using std::complex@<double@>(0,1).

    // Because we work on the unit cell, we can do all this work without a
    // mapping from reference to real cell and consequently do not need the
    // FEValues class.
    for (unsigned int fe=0; fe<fe_collection.size(); ++fe)
      {
        fourier_transform_matrices[fe].reinit (n_fourier_modes,
                                               fe_collection[fe].dofs_per_cell);

        for (unsigned int k=0; k<n_fourier_modes; ++k)
          for (unsigned int j=0; j<fe_collection[fe].dofs_per_cell; ++j)
            {
              std::complex<double> sum = 0;
              for (unsigned int q=0; q<quadrature.size(); ++q)
                {
                  const Point<dim> x_q = quadrature.point(q);
                  sum += std::exp(std::complex<double>(0,1) *
                                  (k_vectors[k] * x_q)) *
                         fe_collection[fe].shape_value(j,x_q) *
                         quadrature.weight(q);
                }
              fourier_transform_matrices[fe](k,j)
                = sum / std::pow(2*numbers::PI, 1.*dim/2);
            }
      }

    // The next thing is to loop over all cells and do our work there, i.e. to
    // locally do the Fourier transform and estimate the decay coefficient. We
    // will use the following two arrays as scratch arrays in the loop and
    // allocate them here to avoid repeated memory allocations:
    std::vector<std::complex<double> > fourier_coefficients (n_fourier_modes);
    Vector<double>                     local_dof_values;

    // Then here is the loop:
    typename hp::DoFHandler<dim>::active_cell_iterator
    cell = dof_handler.begin_active(),
    endc = dof_handler.end();
    for (unsigned int index=0; cell!=endc; ++cell, ++index)
      {
        // Inside the loop, we first need to get the values of the local
        // degrees of freedom (which we put into the
        // local_dof_values array after setting it to the right
        // size) and then need to compute the Fourier transform by multiplying
        // this vector with the matrix ${\cal F}$ corresponding to this finite
        // element. We need to write out the multiplication by hand because
        // the objects holding the data do not have vmult-like
        // functions declared:
        local_dof_values.reinit (cell->get_fe().dofs_per_cell);
        cell->get_dof_values (solution, local_dof_values);

        for (unsigned int f=0; f<n_fourier_modes; ++f)
          {
            fourier_coefficients[f] = 0;

            for (unsigned int i=0; i<cell->get_fe().dofs_per_cell; ++i)
              fourier_coefficients[f] +=
                fourier_transform_matrices[cell->active_fe_index()](f,i)
                *
                local_dof_values(i);
          }

        // The next thing, as explained in the introduction, is that we wanted
        // to only fit our exponential decay of Fourier coefficients to the
        // largest coefficients for each possible value of $|{\bf k}|$. To
        // this end, we create a map that for each magnitude $|{\bf k}|$
        // stores the largest $|\hat U_{{\bf k}}|$ found so far, i.e. we
        // overwrite the existing value (or add it to the map) if no value for
        // the current $|{\bf k}|$ exists yet, or if the current value is
        // larger than the previously stored one:
        std::map<unsigned int, double> k_to_max_U_map;
        for (unsigned int f=0; f<n_fourier_modes; ++f)
          if ((k_to_max_U_map.find (k_vectors_magnitude[f]) ==
               k_to_max_U_map.end())
              ||
              (k_to_max_U_map[k_vectors_magnitude[f]] <
               std::abs (fourier_coefficients[f])))
            k_to_max_U_map[k_vectors_magnitude[f]]
              = std::abs (fourier_coefficients[f]);
        // Note that it comes in handy here that we have stored the magnitudes
        // of vectors as integers, since this way we do not have to deal with
        // round-off-sized differences between different values of $|{\bf
        // k}|$.

        // As the final task, we have to calculate the various contributions
        // to the formula for $\mu$. We'll only take those Fourier
        // coefficients with the largest magnitude for a given value of $|{\bf
        // k}|$ as explained above:
        double  sum_1           = 0,
                sum_ln_k        = 0,
                sum_ln_k_square = 0,
                sum_ln_U        = 0,
                sum_ln_U_ln_k   = 0;
        for (unsigned int f=0; f<n_fourier_modes; ++f)
          if (k_to_max_U_map[k_vectors_magnitude[f]] ==
              std::abs (fourier_coefficients[f]))
            {
              sum_1 += 1;
              sum_ln_k += ln_k[f];
              sum_ln_k_square += ln_k[f]*ln_k[f];
              sum_ln_U += std::log (std::abs (fourier_coefficients[f]));
              sum_ln_U_ln_k += std::log (std::abs (fourier_coefficients[f])) *
                               ln_k[f];
            }

        // With these so-computed sums, we can now evaluate the formula for
        // $\mu$ derived in the introduction:
        const double mu
          = (1./(sum_1*sum_ln_k_square - sum_ln_k*sum_ln_k)
             *
             (sum_ln_k*sum_ln_U - sum_1*sum_ln_U_ln_k));

        // The final step is to compute the Sobolev index $s=\mu-\frac d2$ and
        // store it in the vector of estimated values for each cell:
        smoothness_indicators(index) = mu - 1.*dim/2;
      }
  }
}

// @sect3{The main function}

// The main function is again verbatim what we had before: wrap creating and
// running an object of the main class into a try block and catch
// whatever exceptions are thrown, thereby producing meaningful output if
// anything should go wrong:
int main ()
{
  try
    {
      using namespace dealii;
      using namespace Step27;

      deallog.depth_console (0);

      LaplaceProblem<2> laplace_problem;
      laplace_problem.run ();
    }
  catch (std::exception &exc)
    {
      std::cerr << std::endl << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Exception on processing: " << std::endl
                << exc.what() << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;

      return 1;
    }
  catch (...)
    {
      std::cerr << std::endl << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Unknown exception!" << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;
      return 1;
    }

  return 0;
}


#include <Rcpp.h>

//[[Rcpp::depends(RcppEigen)]]
#include <RcppEigen.h>

//[[Rcpp::depends(BH)]]
#include <boost/math/special_functions/digamma.hpp>

#include "experimentalSetup.hpp"
#include "logLikelihoods.hpp"
#include "individual.hpp"
#include "estimatePoissonGammaParameters.hpp"
#include "AuxiliaryFunctions.hpp"

#include "nlopt.hpp"

// Allele coverage
EstimatePoissonGammaAlleleParameters::EstimatePoissonGammaAlleleParameters(const Eigen::VectorXd & coverage, const std::vector<Eigen::MatrixXd> & expectedContributionMatrix,
                                                                           const std::vector<Eigen::VectorXd> & alleleIndex, const Eigen::VectorXd & markerImbalances,
                                                                           const Eigen::VectorXd & partialSumAlleles, const double & convexMarkerImbalanceInterpolation,
                                                                           const Eigen::VectorXd & tolerance)
{
    Coverage = coverage;
    ExpectedContributionMatrix = expectedContributionMatrix;
    AlleleIndex = alleleIndex;

    PartialSumAlleles = partialSumAlleles;
    MarkerImbalances = markerImbalances;

    ConvexMarkerImbalanceInterpolation = convexMarkerImbalanceInterpolation;
    Tolerance = tolerance;
    MaximumNumberOfIterations = 2000;

    Counter = 0;

    NumberOfContributors = expectedContributionMatrix[0].cols();
    NumberOfMarkers = MarkerImbalances.size();

    initialiseParameters();
}

void EstimatePoissonGammaAlleleParameters::initialiseParameters()
{
    // Initialising mixture parameters
    Eigen::VectorXd phiCurrent = Eigen::VectorXd::Ones(NumberOfContributors) / NumberOfContributors;
    double sumCoverage = 0.0;
    double sumCount = 0.0;
    Eigen::VectorXd sumCoverageMarker = Eigen::VectorXd::Zero(NumberOfMarkers);
    for (std::size_t m = 0; m < NumberOfMarkers; m++)
    {
        const Eigen::VectorXd & AlleleIndex_m = AlleleIndex[m];
        const Eigen::MatrixXd & ExpectedContributionMatrix_m = ExpectedContributionMatrix[m];
        for (std::size_t a = 0; a < AlleleIndex_m.size(); a++)
        {
            std::size_t n = PartialSumAlleles[m] + AlleleIndex_m[a];
            sumCoverageMarker[m] += Coverage[n];
            sumCount++;

            for (std::size_t c = 0; c < NumberOfContributors; c++)
            {
                Eigen::VectorXd contributor_c = ExpectedContributionMatrix_m.col(c);
                if (contributor_c[a] >= 1)
                {
                    phiCurrent[c] += Coverage[n] / contributor_c[a];
                }
            }
        }

        sumCoverage += sumCoverageMarker[m];
    }

    MixtureParameters = phiCurrent / phiCurrent.sum();

    // Initialising sample and marker parameters
    double averageCoverageDenominator = 0.0;
    Eigen::VectorXd markerImbalancesMoM = sumCoverageMarker;
    for (std::size_t m = 0; m < NumberOfMarkers; m++)
    {
        const Eigen::MatrixXd & ExpectedContributionMatrix_m = ExpectedContributionMatrix[m];
        Eigen::VectorXd EC = ExpectedContributionMatrix_m * MixtureParameters;
        const double & ECSum = EC.sum();
        double markerImbalancesMoM_m = markerImbalancesMoM[m];
        if (!(markerImbalancesMoM_m > 0))
        {
            markerImbalancesMoM_m = 1e-6;
        }

        averageCoverageDenominator += ECSum;
        markerImbalancesMoM[m] = markerImbalancesMoM_m / ECSum;
    }

    SampleParameters = 2.0 * Eigen::VectorXd::Ones(2);
    SampleParameters[0] = sumCoverage / averageCoverageDenominator;

    markerImbalancesMoM = markerImbalancesMoM / SampleParameters[0];
    markerImbalancesMoM = markerImbalancesMoM / markerImbalancesMoM.mean();

    MarkerImbalancesParameters = ConvexMarkerImbalanceInterpolation * markerImbalancesMoM + (1 - ConvexMarkerImbalanceInterpolation) * MarkerImbalances;
}

double logLikelihoodAlleleCoverageNLopt(const std::vector<double> & x, std::vector<double> & grad, void *data)
{
    // Unpacking data
    EstimatePoissonGammaAlleleParameters *EPGA = reinterpret_cast<EstimatePoissonGammaAlleleParameters*>(data);
    const std::vector<Eigen::MatrixXd> & ExpectedContributionMatrix = EPGA->ExpectedContributionMatrix;
    const std::vector<Eigen::VectorXd> & AlleleIndex = EPGA->AlleleIndex;

    const Eigen::VectorXd & MarkerImbalances = EPGA->MarkerImbalancesParameters;
    const Eigen::VectorXd & PartialSumAlleles = EPGA->PartialSumAlleles;
    const Eigen::VectorXd & Coverage = EPGA->Coverage;

    const std::size_t NumberOfMarkers = EPGA->NumberOfMarkers;

    std::size_t & Counter = EPGA->Counter;

    const std::size_t & C = EPGA->NumberOfContributors;
    const std::size_t S = 2;

    const double & referenceMarkerAverage = x[0];
    const double & dispersion = x[1];

    double logLikelihood = 0.0;
    std::vector<double> gradient(C + S - 1, 0.0);
    for (std::size_t m = 0; m < NumberOfMarkers; m++)
    {
        const Eigen::VectorXd & AlleleIndex_m = AlleleIndex[m];
        const Eigen::MatrixXd & ExpectedContributionMatrix_m = ExpectedContributionMatrix[m];
        for (std::size_t a = 0; a < AlleleIndex_m.size(); a++)
        {
            std::size_t n = PartialSumAlleles[m] + AlleleIndex_m[a];
            const Eigen::VectorXd ExpectedContributionMatrix_ma = ExpectedContributionMatrix_m.row(a);

            double EC_n = ExpectedContributionMatrix_ma[C - 1];
            for (std::size_t c = 0; c < C - 1; c++)
            {
                EC_n += (ExpectedContributionMatrix_ma[c] - ExpectedContributionMatrix_ma[C - 1]) * x[S + c];
            }

            double mu_ma = referenceMarkerAverage * MarkerImbalances[m] * EC_n;
            if (!(mu_ma > 0.0))
            {
                mu_ma += 2e-16;
            }

            logLikelihood += logPoissonGammaDistribution(Coverage[n], mu_ma, mu_ma / dispersion);

            if (!grad.empty())
            {
                const double & gradient_common_term = boost::math::digamma(Coverage[n] + mu_ma / dispersion) - boost::math::digamma(mu_ma / dispersion) - std::log(dispersion + 1.0);

                gradient[0] += gradient_common_term * (MarkerImbalances[m] * EC_n) / (dispersion);
                gradient[1] += (Coverage[n] - mu_ma) / (dispersion * (dispersion + 1.0)) - gradient_common_term * (mu_ma / (std::pow(dispersion, 2.0)));

                for (std::size_t c = 0; c < C - 1; c++)
                {
                    const double & EC_nm = ExpectedContributionMatrix_ma[c] - ExpectedContributionMatrix_ma[C - 1];
                    gradient[S + c] +=  gradient_common_term * ((referenceMarkerAverage * MarkerImbalances[m] * EC_nm) / dispersion);
                }

            }
        }
    }

    if (!grad.empty())
    {
        grad = gradient;
    }

    Counter++;
    return logLikelihood;
}

void estimateParametersAlleleCoverage(EstimatePoissonGammaAlleleParameters &EPGA)
{
    std::size_t S = EPGA.SampleParameters.size();
    std::size_t C = EPGA.MixtureParameters.size();

    std::vector<double> parameters(S + C - 1, 0.0);
    for (std::size_t s = 0; s < S; s++)
    {
        parameters[s] = EPGA.SampleParameters[s];
    }

    for (std::size_t c = 0; c < C - 1; c++)
    {
        parameters[S + c] = EPGA.MixtureParameters[c];
    }

    // Optimiser
    // nlopt::opt individualOptimisation(nlopt::LN_BOBYQA, S + C - 1);
    // nlopt::opt individualOptimisation(nlopt::LD_LBFGS, S + C - 1);
    nlopt::opt individualOptimisation(nlopt::LD_SLSQP, S + C - 1);

    // Box-constraints
    std::size_t N = EPGA.Coverage.size();
    std::vector<double> lowerBound(S + C - 1), upperBound(S + C - 1);
    lowerBound[0] = 1;
    lowerBound[1] = 2e-8;
    upperBound[0] = EPGA.Coverage.maxCoeff() + 1;
    upperBound[1] = std::exp(std::log(2.0) + std::log(N) - std::log(2.0 * N - 2.0))  * (std::pow(EPGA.Coverage.maxCoeff(), 2.0) + 1);

    if (C == 1)
    {
        lowerBound[S] = 1 - 2e-16;
        upperBound[S] = 1;
    }
    else
    {
        for (std::size_t j = 0; j < C - 1; j++)
        {
            lowerBound[S + j] = 2e-16;
            upperBound[S + j] = 1.0 - 2e-16;
        }
    }

    individualOptimisation.set_lower_bounds(lowerBound);
    individualOptimisation.set_upper_bounds(upperBound);

    // Objective function
    individualOptimisation.set_max_objective(logLikelihoodAlleleCoverageNLopt, &EPGA);

    individualOptimisation.set_ftol_rel(EPGA.Tolerance[0]);
    individualOptimisation.set_ftol_abs(EPGA.Tolerance[1]);
    individualOptimisation.set_xtol_rel(EPGA.Tolerance[2]);
    individualOptimisation.set_xtol_abs(EPGA.Tolerance[3]);

    individualOptimisation.set_maxeval(EPGA.MaximumNumberOfIterations);

    double logLikelihood;
    try
    {
        nlopt::result result = individualOptimisation.optimize(parameters, logLikelihood);
    }
    catch (...)
    {
        try
        {
            if (EPGA.Tolerance[0] > 0)
                individualOptimisation.set_ftol_rel(std::pow(10, (-2 + std::log10(EPGA.Tolerance[0])) / 2));

            if (EPGA.Tolerance[1] > 0)
                individualOptimisation.set_ftol_abs(std::pow(10, (-2 + std::log10(EPGA.Tolerance[1])) / 2));

            if (EPGA.Tolerance[2] > 0)
                individualOptimisation.set_xtol_rel(std::pow(10, (-2 + std::log10(EPGA.Tolerance[2])) / 2));

            if (EPGA.Tolerance[3] > 0)
                individualOptimisation.set_xtol_abs(std::pow(10, (-2 + std::log10(EPGA.Tolerance[3])) / 2));

            nlopt::result result = individualOptimisation.optimize(parameters, logLikelihood);
        }
        catch (...)
        {
            //
        }
    }

    EPGA.LogLikelihood = logLikelihood;
    Eigen::VectorXd Parameters = STDEigen(parameters);

    EPGA.SampleParameters = Parameters.segment(0, S);

    EPGA.MixtureParameters.segment(0, C - 1) = Parameters.segment(S, C - 1);
    EPGA.MixtureParameters[C - 1] =  1.0 - Parameters.segment(S, C - 1).sum();
}

// Noise coverage
EstimatePoissonGammaNoiseParameters::EstimatePoissonGammaNoiseParameters(const Eigen::VectorXd & coverage,
                                                                         const std::vector<Eigen::VectorXd> & noiseIndex,
                                                                         const Eigen::VectorXd & partialSumAlleles,
                                                                         const Eigen::VectorXd & tolerance,
                                                                         const double & varianceUpperLimit)
{
    Coverage = coverage;
    NoiseIndex = noiseIndex;

    PartialSumAlleles = partialSumAlleles;

    NumberOfMarkers = NoiseIndex.size();
    Tolerance = tolerance;
    MaximumNumberOfIterations = 2000;
    VarianceUpperLimit = varianceUpperLimit;

    Counter = 0;

    initialiseParameters();
}

void EstimatePoissonGammaNoiseParameters::initialiseParameters()
{
    Eigen::VectorXd parameters = Eigen::VectorXd::Ones(3);

    double noiseCoverageSum = 0.0;
    double noiseCoverageSquaredSum = 0.0;
    double noiseCoverageSize = 0.0;
    double noiseCoverageInflation = 0.0;
    for (std::size_t m = 0; m < NumberOfMarkers; m++)
    {
        const Eigen::VectorXd & NoiseIndex_m = NoiseIndex[m];
        for (std::size_t a = 0; a < NoiseIndex_m.size(); a++)
        {
            std::size_t n = PartialSumAlleles[m] + NoiseIndex_m[a];
            noiseCoverageSum += Coverage[n];
            noiseCoverageSquaredSum += std::pow(Coverage[n], 2.0);
            noiseCoverageSize++;

            if (Coverage[n] == 1.0)
                noiseCoverageInflation++;
        }
    }

    double averageNoiseCoverage = std::ceil(0.5 * noiseCoverageSum) / noiseCoverageSize;

    parameters[0] = averageNoiseCoverage * (1 - std::exp(logPoissonGammaDistribution(0, averageNoiseCoverage, averageNoiseCoverage)));
    parameters[1] = 1.0; // std::abs((noiseCoverageSquaredSum / noiseCoverageSize - std::pow(parameters[0], 2.0)) / parameters[0] - 1.0);
    parameters[2] = noiseCoverageInflation / (2.0 * noiseCoverageSize);

    if (parameters[0] < 1.0) {
        parameters[0] = 1.0;
    }

    if (parameters[1] < 1.0) {
        parameters[1] = 1.0;
    }

    if (parameters[1] > VarianceUpperLimit) {
        parameters[1] = VarianceUpperLimit;
    }

    NoiseParameters = parameters;
}

double logLikelihoodNoiseCoverageNLopt(const std::vector<double> &x, std::vector<double> &grad, void *data)
{
    EstimatePoissonGammaNoiseParameters *EPGN = reinterpret_cast<EstimatePoissonGammaNoiseParameters*>(data);
    const Eigen::VectorXd & Coverage = EPGN->Coverage;
    const std::vector<Eigen::VectorXd> & NoiseIndex = EPGN->NoiseIndex;
    const Eigen::VectorXd & PartialSumAlleles = EPGN->PartialSumAlleles;
    const std::size_t & NumberOfMarkers = EPGN->NumberOfMarkers;
    std::size_t & Counter = EPGN->Counter;

    const double & mu_ma = x[0];
    const double & dispersion = x[1];
    const double & p = x[2];

    double logLikelihood = 0.0;
    for (std::size_t m = 0; m < NumberOfMarkers; m++)
    {
        const Eigen::VectorXd & NoiseIndex_m = NoiseIndex[m];
        for (std::size_t a = 0; a < NoiseIndex_m.size(); a++)
        {
            std::size_t n = PartialSumAlleles[m] + NoiseIndex_m[a];
            logLikelihood += logInflatedTruncatedPoissonGammaDistribution(Coverage[n], mu_ma, dispersion, p, 1.0, 0.0);
                //- std::log(1 - std::exp(dispersion * logeta)); // std::log(1.0 - std::exp(gamma * zero_truncation)); //
        }
    }

    Counter++;
    return logLikelihood;
}

void estimateParametersNoiseCoverage(EstimatePoissonGammaNoiseParameters &EPGN)
{
    const std::size_t & N = EPGN.NoiseParameters.size();
    std::vector<double> parameters = EigenSTD(EPGN.NoiseParameters);

    // Optimiser
    // nlopt::opt individualOptimisation(nlopt::LN_BOBYQA, N);
    nlopt::opt individualOptimisation(nlopt::LN_SBPLX, N);

    // Box-constraints
    std::vector<double> lowerBound(N), upperBound(N);
    lowerBound[0] = 1.0;
    lowerBound[1] = 1.0;
    lowerBound[2] = 2e-16;

    upperBound[0] = EPGN.Coverage.maxCoeff();
    upperBound[1] = EPGN.VarianceUpperLimit; // (EPGN.Coverage.size() / (EPGN.Coverage.size() - 1.0)) * std::pow(EPGN.Coverage.maxCoeff(), 2.0);
    upperBound[2] = 1.0 - 2e-16;

    individualOptimisation.set_lower_bounds(lowerBound);
    individualOptimisation.set_upper_bounds(upperBound);

    // Objective function
    individualOptimisation.set_max_objective(logLikelihoodNoiseCoverageNLopt, &EPGN);

    individualOptimisation.set_ftol_rel(EPGN.Tolerance[0]);
    individualOptimisation.set_ftol_abs(EPGN.Tolerance[1]);
    individualOptimisation.set_xtol_rel(EPGN.Tolerance[2]);
    individualOptimisation.set_xtol_abs(EPGN.Tolerance[3]);

    individualOptimisation.set_maxeval(EPGN.MaximumNumberOfIterations);

    double logLikelihood;
    try
    {
        nlopt::result result = individualOptimisation.optimize(parameters, logLikelihood);
    }
    catch (...)
    {
        try
        {
            if (EPGN.Tolerance[0] > 0)
                individualOptimisation.set_ftol_rel(std::pow(10, (-2 + std::log10(EPGN.Tolerance[0])) / 2));

            if (EPGN.Tolerance[1] > 0)
                individualOptimisation.set_ftol_abs(std::pow(10, (-2 + std::log10(EPGN.Tolerance[1])) / 2));

            if (EPGN.Tolerance[2] > 0)
                individualOptimisation.set_xtol_rel(std::pow(10, (-2 + std::log10(EPGN.Tolerance[2])) / 2));

            if (EPGN.Tolerance[3] > 0)
                individualOptimisation.set_xtol_abs(std::pow(10, (-2 + std::log10(EPGN.Tolerance[3])) / 2));

            nlopt::result result = individualOptimisation.optimize(parameters, logLikelihood);
        }
        catch (...)
        {
            //
        }
    }

    EPGN.LogLikelihood = logLikelihood;
    Eigen::VectorXd Parameters = STDEigen(parameters);
    EPGN.NoiseParameters = Parameters;
}


/* This module defines overall interface to the generalized Ising solver.
 * See the README for the meaning of input flags, input files, and installation
 *
 * Author: Wenxuan Huang
 * Maintainer: Wenxuan Huang, Daniil Kitchaev
 * Date: 15 March, 2015
 *
 */

#include <iostream>
#include <fstream>
#include <sstream>
#include <streambuf>
#include <string>
#include <stdio.h>
#include <vector>
#include <set>
#include <map>
#include "gurobi_c++.h"
#include "boost/tuple/tuple.hpp"
#include "boost/tuple/tuple_comparison.hpp"
#include "boost/tuple/tuple_io.hpp"
#include <boost/lexical_cast.hpp>
#include "solver.h"

using namespace std;
using namespace ::boost::tuples;
using namespace ::boost;

void read_from_file(std::string &id,
                     int &max_sites,
                     map< set<tuple<int,int,int,int,int> >, double> &J,
                     double &prec,
                     int &num_loops,
                     bool &translation_algorithm,
                     bool &basic_exact_mode,
                     bool &pseudo_mode,
                     bool &pseudo_mode_with_proof,
                     bool &verbose,
                     bool &very_verbose,
                     bool &obscenely_verbose);

int main (void) {
    std::string id="IS0";
    int max_sites = 50;
    int num_loops = 4;
    double prec = 0.00001;
    bool translation_algorithm = false;
    bool basic_exact_mode = false;
    bool pseudo_mode = true;
    bool pseudo_mode_with_proof = false;
    bool verbose = true;
    bool very_verbose = false;
    bool obscenely_verbose = false;

    double lower_bound, upper_bound, exact_lowerbound;

    map< set<tuple<int,int,int,int,int> >, double> J, lowerboundclustertype, upperboundclustertype, J_for_proof;
    map< tuple<int,int,int,int>,int> unitcell;
    tuple<int,int,int,int,int,int> periodicity;
    map< tuple<int,int,int,int>, int> cellrepresentation;

    read_from_file(id, max_sites, J, prec, num_loops, translation_algorithm,
                   basic_exact_mode, pseudo_mode, pseudo_mode_with_proof,
                   verbose, very_verbose, obscenely_verbose);

    run_solver(max_sites, J,
               lowerboundclustertype, upperboundclustertype, cellrepresentation,
               lower_bound, upper_bound, exact_lowerbound, unitcell, periodicity,
               J_for_proof,
               id,
               prec, num_loops,
               basic_exact_mode, pseudo_mode, pseudo_mode_with_proof,
               verbose, very_verbose, obscenely_verbose);

    return 0;
}

void read_from_file(std::string &id,
                     int &max_sites,
                     map< set<tuple<int,int,int,int,int> >, double> &J,
                     double &prec,
                     int &num_loops,
                     bool &translation_algorithm,
                     bool &basic_exact_mode,
                     bool &pseudo_mode,
                     bool &pseudo_mode_with_proof,
                     bool &verbose,
                     bool &very_verbose,
                     bool &obscenely_verbose) {
	map< set<tuple<int,int,int,int,int> >, double> J_negative1_positive1;
	set< tuple<int,int,int,int,int> >setoftupletemp;
	double constant_term = 0;
	int solver_mode = 1;
	int verbosity = 1;

	std::ifstream t2("J_config.in");
	std::stringstream buffer2;
	buffer2 << t2.rdbuf();
	string J_config_file=buffer2.str();

	vector<string> J_config_line;
	split_is(J_config_file, '\n', J_config_line);
	for (vector<string>::iterator it=J_config_line.begin(); it!=J_config_line.end(); it++) {
		vector<string> J_config_line_segment;
		string temp=*it;
		split_is(temp, ' ', J_config_line_segment);
		vector<string>::iterator segment_iterator=J_config_line_segment.begin();
		string first_segment=*segment_iterator;
		vector<string> equal_left_right;
		split_is(first_segment, '=', equal_left_right);
		vector<string>::iterator equal_iterator=equal_left_right.begin();
		string equal_left=*equal_iterator;
		equal_iterator++;
		string equal_right=*equal_iterator;

		if (equal_left=="NSITES") {
			max_sites=lexical_cast<int>(equal_right) ;
		}else if (equal_left=="NLOOPS") {
			num_loops=lexical_cast<int>(equal_right) ;
		}else if (equal_left=="LABEL") {
			id=equal_right;
		}else if (equal_left=="PREC") {
			prec=lexical_cast<double>(equal_right) ;
		}else if (equal_left=="MODE_JPLUSMINUS") {
			translation_algorithm=lexical_cast<bool>(equal_right) ;
		}else if (equal_left=="MODE_SOLVER") {
			solver_mode=lexical_cast<int>(equal_right) ;
		}else if (equal_left=="MODE_VERBOSITY") {
			verbosity = lexical_cast<int>(equal_right) ;
		}
	}

	if (solver_mode == 0) {
		basic_exact_mode = true;
		pseudo_mode = false;
		pseudo_mode = false;
	}else if (solver_mode == 1) {
		basic_exact_mode = false;
		pseudo_mode = true;
		pseudo_mode_with_proof = false;
	}else if (solver_mode == 2) {
		basic_exact_mode = false;
		pseudo_mode = true;
		pseudo_mode_with_proof = true;
	}else{
		cout << "Invalid solver mode given. Exiting." << endl;
		exit(1);
	}

	if (verbosity == 0) {
		verbose = false;
		very_verbose = false;
		obscenely_verbose = false;
	}else if (verbosity == 1) {
		verbose = true;
		very_verbose = false;
		obscenely_verbose = false;
	}else if (verbosity == 2) {
		verbose = true;
		very_verbose = true;
		obscenely_verbose = false;
	}else if (verbosity == 3) {
		verbose = true;
		very_verbose = true;
		obscenely_verbose = true;
	}else {
		cout << "Invalid verbosity mode given. Exiting." << endl;
		exit(1);
	}

	std::ifstream t1("J_in.in");
	std::stringstream buffer1;
	buffer1 << t1.rdbuf();
	string J_in_file=buffer1.str();

	vector<string> J_in_line;
	split_is(J_in_file, '\n', J_in_line);
	int line_format_indicator=0;
	for (vector<string>::iterator it=J_in_line.begin(); it!=J_in_line.end(); it++){
		string this_line=*it;
		if (it==J_in_line.begin()){
			vector<string> segment;
			split_is(this_line, ' ', segment);
			vector<string>::iterator segment_iterator=segment.begin();
			string temp_segment=*segment_iterator;
			if (temp_segment=="Constant"){
				segment_iterator++;
				constant_term=lexical_cast<double>(*segment_iterator);
			}
		}else{
			if (line_format_indicator==0) {
				vector<string> segment;
				split_is(this_line, ' ', segment);
				vector<string>::iterator segment_iterator=segment.begin();
				string temp_segment=*segment_iterator;
				if (temp_segment=="Cluster") {
					line_format_indicator=1;
				}
			}else if (line_format_indicator==1){
				setoftupletemp.clear();
				vector<string> segment;
				split_is(this_line, ' ', segment);

				for (vector<string>::iterator it2=segment.begin(); it2!=segment.end(); it2++) {
					string this_segment=*it2;
					vector<string> number_vector;
					split_is(this_segment, ',', number_vector);
					int l1,l2,l3,l4,l5;
					l1=lexical_cast<int>(number_vector[0]);
					l2=lexical_cast<int>(number_vector[1]);
					l3=lexical_cast<int>(number_vector[2]);
					l4=lexical_cast<int>(number_vector[3]);
					l5=lexical_cast<int>(number_vector[4]);
					setoftupletemp.insert(make_tuple(l1,l2,l3,l4,l5));
				}

				line_format_indicator=2;
			}else if (line_format_indicator==2) {
				vector<string> segment;
				split_is(this_line, '=', segment);

				if (translation_algorithm){
					J_negative1_positive1[setoftupletemp]=lexical_cast<double>(segment[1]);
				}else{
					J[setoftupletemp]=lexical_cast<double>(segment[1]);
				}

				line_format_indicator=0;
			}
		}
	}

	if(translation_algorithm){
		convertJ_neg1_pos1toJ(J_negative1_positive1, J, constant_term);
	}
}


/*

Copyright (c) 2005-2022, University of Oxford.
All rights reserved.

University of Oxford means the Chancellor, Masters and Scholars of the
University of Oxford, having an administrative office at Wellington
Square, Oxford OX1 2JD, UK.

This file is part of Chaste.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.
 * Neither the name of the University of Oxford nor the names of its
   contributors may be used to endorse or promote products derived from this
   software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

#include <cmath>
#include <iostream>
#include <boost/math/tools/roots.hpp>
#include <boost/bind.hpp>

#include "BoostTolerance.hpp"
#include "HiornsAirwayWall.hpp"
#include "MathsCustomFunctions.hpp"
#include "Exception.hpp"

HiornsAirwayWall::HiornsAirwayWall() : mTargetPressure(0),
                                       mRIn(0),
                                       mROut(0),
                                       mmu(0),
                                       mphi1(0),
                                       mphi2(0),
                                       mC1(0),
                                       mC2(0),
                                       mA(0)
{
}

HiornsAirwayWall::~HiornsAirwayWall() {}

void HiornsAirwayWall::SetTimestep(double dt) {}

double HiornsAirwayWall::CalculatePressureRadiusResidual(double radius)
{

    mTargetPressure = mAirwayPressure - mPleuralPressure;

    double rin = radius;

    double areaOfAirwayWall = M_PI*(mROut*mROut - mRIn*mRIn);
    double rout = sqrt(rin*rin + areaOfAirwayWall/M_PI);
    double pressure;

    pressure = mmu*log((rin*mROut)/(rout*mRIn)) + mmu*((rin*rin - mRIn*mRIn)/2.)*((1./rin*rin) - (1./rout*rout)) + 2*cos(mphi2)*cos(mphi2)*mA*log(mROut/mRIn);

    if (rin - mRIn > 0.)
        {

        //  THE COMMENTED OUT CODE CORRESPONDS TO THE PRELIMINARY VERSION OF THE HIORNS QUASI-STATIC
        //  AIRWAY WALL MODEL (I.E. EQUATION 1.1 IN THE HIORNS LOOKUP TABLES NOTES). THE VERSION THAT
        //  WE USE (FOLLOWING THE COMMENTED SECTION) INSTEAD IS MODIFIED SO THAT THE CONTRIBUTION OF
        //  THE COLLAGEN TO THE STRAIN-ENERGY LAW SATISFIES THE MODEL OF HOLZAPFEL ET AL.

        //  const int numberTrap = 10000;
        //  double integrandVals[numberTrap];
        //  double tVals[numberTrap];
        //  double integralStuff = 0.;

        //  double upperlimit = -1.;
        //  double lowerlimit = -1.;

        //  lowerlimit = ((sqrt(mC2)*(rout*rout - mROut*mROut)*cos(mphi1)*cos(mphi1))/((mROut)*(mROut)));
        //  upperlimit = ((sqrt(mC2)*(rin*rin - mRIn*mRIn)*cos(mphi1)*cos(mphi1))/((mRIn)*(mRIn)));

        //  for (int it = 0; it < numberTrap; it++)
        //  {

            //  double tVal = lowerlimit + ((double)it/((double)numberTrap - 1.))*(upperlimit - lowerlimit);
            //  tVals[it] = tVal;
            //  integrandVals[it] = (2.*exp(tVal*tVal)/sqrt(M_PI));

        //  }

        //  integralStuff = (0.5*(integrandVals[0] + integrandVals[numberTrap - 1]));
        //  integralStuff = integralStuff*(tVals[1] - tVals[0]);
        //  for (int i = 1; i < (numberTrap - 1); i++)
        //  {
            //  integralStuff = integralStuff + integrandVals[i]*(tVals[1] - tVals[0]);
        //  }

        //  pressure = pressure + mC1*sqrt(M_PI/mC2)*cos(mphi1)*cos(mphi1)*integralStuff;

            double integralStuff = exp(mC2*((rin*rin - mRIn*mRIn)/(mRIn*mRIn))*((rin*rin - mRIn*mRIn)/(mRIn*mRIn))*cos(mphi1)*cos(mphi1)*cos(mphi1)*cos(mphi1)) - exp(mC2*((rout*rout - mROut*mROut)/(mROut*mROut))*((rout*rout - mROut*mROut)/(mROut*mROut))*cos(mphi1)*cos(mphi1)*cos(mphi1)*cos(mphi1));
            pressure = pressure + (mC1/mC2)*cos(mphi1)*cos(mphi1)*integralStuff;

        }

    double residual = mTargetPressure - pressure;

    return residual;

}

void HiornsAirwayWall::SolveAndUpdateState(double tStart, double tEnd)
{

    double guess = (mRIn + mROut)/2.;
    double factor = 2.;

    Tolerance tol = 0.000001;
    boost::uintmax_t maxIterations = 500u;

    std::pair<double, double> found = boost::math::tools::bracket_and_solve_root(boost::bind(&HiornsAirwayWall::CalculatePressureRadiusResidual, this, _1), guess, factor, false, tol, maxIterations);
    mDeformedAirwayRadius = found.first;
}

void HiornsAirwayWall::SetRIn(double RIn)
{
    assert (RIn >= 0.0);
    mRIn = RIn;
}

void HiornsAirwayWall::SetROut(double ROut)
{
    assert (ROut >= 0.0);
    mROut = ROut;
}

void HiornsAirwayWall::Setmu(double mu)
{
    assert (mu >= 0.0);
    mmu = mu;
}

void HiornsAirwayWall::Setphi1(double phi1)
{
    mphi1 = phi1;
}

void HiornsAirwayWall::Setphi2(double phi2)
{
    mphi2 = phi2;
}

void HiornsAirwayWall::SetC1(double C1)
{
    mC1 = C1;
}

void HiornsAirwayWall::SetC2(double C2)
{
    mC2 = C2;
}

void HiornsAirwayWall::SetA(double A)
{
    mA = A;
}

double HiornsAirwayWall::GetLumenRadius()
{
    return mDeformedAirwayRadius;
}

void HiornsAirwayWall::SetAirwayPressure(double pressure)
{
    mAirwayPressure = pressure;
}

void HiornsAirwayWall::SetPleuralPressure(double pressure)
{
    mPleuralPressure = pressure;
}


/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*!
Calibration of Heston model on SPX vol surface
*/

#include <ql/quantlib.hpp>
#include "CsvHelper.hpp"
#include "HestonTools.hpp"

#ifdef BOOST_MSVC
/* Uncomment the following lines to unmask floating-point
   exceptions. Warning: unpredictable results can arise...

   See http://www.wilmott.com/messageview.cfm?catid=10&threadid=9481
   Is there anyone with a definitive word about this?
*/
// #include <float.h>
// namespace { unsigned int u = _controlfp(_EM_INEXACT, _MCW_EM); }
#endif

#include <boost/timer.hpp>
#include <iostream>
#include <iomanip>
#include <string>
#include <fstream>

#define DEBUG_OUT true

using namespace QuantLib;

#if defined(QL_ENABLE_SESSIONS)
namespace QuantLib
{

Integer sessionId()
{
    return 0;
}

}
#endif

int main(int, char* [])
{

    Handle<Quote> s0(boost::shared_ptr<Quote>(new SimpleQuote(1290.59)));

    try
    {

// calibration data

        std::fstream file1("/home/phn/dev/R/ValidationModeles/calibration.csv", ios::in);
        if (!file1.is_open())
        {
            std::cout << "Calibration File not found!\n";
            return 1;
        }

        std::vector<CALIB> calibData;
        readCSV_Calib(file1, calibData);
        file1.close();

// interest rates and dividend yield

        std::fstream file2("/home/phn/dev/R/ValidationModeles/rate_div.csv", ios::in);
        if (!file2.is_open())
        {
            std::cout << "Rate/Yield File not found!\n";
            return 1;
        }

        std::vector<RD> RDData;
        readCSV_RD(file2, RDData);
        file2.close();

        std::vector<double> res;

        res = HestonCalibration(s0, calibData, RDData);

        if (DEBUG_OUT)
            std::cout << "Theta: " << res[0] <<
                      "\nKappa: " << res[1] <<
                      "\nsigma: " << res[2] <<
                      "\nrho: " << res[3] <<
                      "\nv0: " << res[4] <<
                      "\nsse: " << res[5] << std::endl;

    }
    catch (std::exception& e)
    {
        std::cerr << e.what() << std::endl;
        return 1;
    }
    catch (...)
    {
        std::cerr << "unknown error" << std::endl;
        return 1;
    }
}


#include "RooRealVar.h"
#include "RooRealConstant.h"
#include "RooGaussian.h"
#include "RooExponential.h"
#include "RooAddPdf.h"
#include "RooAddition.h"
#include "RooDataSet.h"
#include "RooDataHist.h"
#include "RooHistPdf.h"
#include "RooChebychev.h"
#include "RooExponential.h"
#include "RooProdPdf.h"
#include "RooChi2Var.h"
#include "RooGlobalFunc.h"
#include "RooPlot.h"
#include "RooMinuit.h"
#include "RooFitResult.h"
#include "RooFormulaVar.h"
#include "RooGenericPdf.h"
#include "RooExtendPdf.h"
#include "TCanvas.h"
#include "TROOT.h"
#include "TFile.h"
#include "TH1.h"
#include "RooNLLVar.h"
#include "RooRandom.h"
#include "TRandom3.h"
#include <iostream>
#include <fstream>
#include <boost/program_options.hpp>
using namespace boost;
namespace po = boost::program_options;
#include <vector>

using namespace std;
using namespace RooFit;

// A function that get histogram, restricting it on fit range  and sets contents to 0.0 if entries are too small

TH1F *getHisto(TFile *file, const char *name, double fMin, double fMax, unsigned int rebin) {
  TObject *h = file->Get(name);
  if (h == nullptr)
    cout << "Can't find object " << name << "\n";
  TH1F *histo = dynamic_cast<TH1F *>(h);
  if (histo == nullptr)
    cout << "Object " << name << " is of type " << h->ClassName() << ", not TH1\n";
  TH1F *new_histo = new TH1F(name, name, (int)(fMax - fMin), fMin, fMax);
  int bin_num = 0;
  for (int i = (int)fMin; i <= (int)fMax; ++i) {
    bin_num = (i - (int)fMin + 1);
    new_histo->SetBinContent(bin_num, histo->GetBinContent(i));
  }
  delete histo;
  new_histo->Sumw2();
  new_histo->Rebin(rebin);
  for (int i = 1; i <= new_histo->GetNbinsX(); ++i) {
    if (new_histo->GetBinContent(i) == 0.00) {
      cout << " WARNING: histo " << name << " has 0 enter in bin number " << i << endl;
    }
    if (new_histo->GetBinContent(i) < 0.1) {
      new_histo->SetBinContent(i, 0.0);
      new_histo->SetBinError(i, 0.0);
      cout << " WARNING: setting value 0.0 to histo " << name << " for bin number " << i << endl;
    }
  }

  return new_histo;
}

// a function that create fromm a model pdf a toy RooDataHist

RooDataHist *genHistFromModelPdf(
    const char *name, RooAbsPdf *model, RooRealVar *var, double ScaleLumi, int range, int rebin, int seed) {
  double genEvents = model->expectedEvents(*var);
  TRandom3 *rndm = new TRandom3();
  rndm->SetSeed(seed);
  double nEvt = rndm->PoissonD(genEvents);
  int intEvt = ((nEvt - (int)nEvt) >= 0.5) ? (int)nEvt + 1 : int(nEvt);
  RooDataSet *data = model->generate(*var, intEvt);
  cout << " expected events for " << name << " = " << genEvents << endl;
  cout << " data->numEntries() for name " << name << " == " << data->numEntries() << endl;
  // cout<< " nEvt from PoissonD for" << name << " == " << nEvt<< endl;
  //cout<< " cast of nEvt  for" << name << " == " << intEvt<< endl;
  RooAbsData *binned_data = data->binnedClone();
  TH1 *toy_hist = binned_data->createHistogram(name, *var, Binning(range / rebin));
  for (int i = 1; i <= toy_hist->GetNbinsX(); ++i) {
    toy_hist->SetBinError(i, sqrt(toy_hist->GetBinContent(i)));
    if (toy_hist->GetBinContent(i) == 0.00) {
      cout << " WARNING: histo " << name << " has 0 enter in bin number " << i << endl;
    }
    if (toy_hist->GetBinContent(i) < 0.1) {
      toy_hist->SetBinContent(i, 0.0);
      toy_hist->SetBinError(i, 0.0);
      cout << " WARNING: setting value 0.0 to histo " << name << " for bin number " << i << endl;
    }
  }
  RooDataHist *toy_rooHist = new RooDataHist(name, name, RooArgList(*var), toy_hist);
  return toy_rooHist;
}

// a function to create the pdf used for the fit, need the histo model, should be zmm except for zmusta case.....

RooHistPdf *createHistPdf(const char *name, TH1F *model, RooRealVar *var, int rebin) {
  TH1F *model_clone = new TH1F(*model);
  model_clone->Sumw2();
  model_clone->Rebin(rebin);
  RooDataHist *model_dataHist = new RooDataHist(name, name, RooArgList(*var), model_clone);
  RooHistPdf *HistPdf = new RooHistPdf(name, name, *var, *model_dataHist, 0);
  delete model_clone;
  return HistPdf;
}

void fit(RooAbsReal &chi2, int numberOfBins, const char *outFileNameWithFitResult) {
  TFile *out_root_file = new TFile(outFileNameWithFitResult, "recreate");
  RooMinuit m_tot(chi2);
  m_tot.migrad();
  // m_tot.hesse();
  RooFitResult *r_chi2 = m_tot.save();
  cout << "==> Chi2 Fit results " << endl;
  r_chi2->Print("v");
  //  r_chi2->floatParsFinal().Print("v") ;
  int NumberOfFreeParameters = r_chi2->floatParsFinal().getSize();
  for (int i = 0; i < NumberOfFreeParameters; ++i) {
    r_chi2->floatParsFinal()[i].Print();
  }
  cout << "chi2:" << chi2.getVal() << ", numberOfBins: " << numberOfBins
       << ", NumberOfFreeParameters: " << NumberOfFreeParameters << endl;
  cout << "Normalized Chi2   = " << chi2.getVal() / (numberOfBins - NumberOfFreeParameters) << endl;
  r_chi2->Write();
  delete out_root_file;
}

int main(int argc, char **argv) {
  gROOT->SetStyle("Plain");
  double fMin, fMax, lumi, scaleLumi = 1;
  int seed;
  Bool_t toy = kFALSE;
  Bool_t fitFromData = kFALSE;
  string infile, outfile;
  int rebinZMuMu = 1, rebinZMuSa = 1, rebinZMuTk = 1, rebinZMuMuNoIso = 1, rebinZMuMuHlt = 1;
  po::options_description desc("Allowed options");
  desc.add_options()("help,h", "produce help message")("toy,t", "toy enabled")(
      "seed,s", po::value<int>(&seed)->default_value(34567), "seed value for toy")(
      "luminosity,l", po::value<double>(&lumi)->default_value(45.), "luminosity value for toy ")(
      "fit,f", "fit from data enabled")(
      "rebin,r", po::value<vector<int> >(), "rebin value: r_mutrk r mumuNotIso r_musa r _muhlt")(
      "input-file,i", po::value<string>(&infile), "input file")(
      "output-file,o", po::value<string>(&outfile), "output file with fit results")(
      "min,m", po::value<double>(&fMin)->default_value(60), "minimum value for fit range")(
      "max,M", po::value<double>(&fMax)->default_value(120), "maximum value for fit range");
  po::positional_options_description p;
  p.add("rebin", -1);
  po::variables_map vm;
  po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), vm);
  po::notify(vm);
  if (vm.count("help")) {
    cout << "Usage: options_description [options]\n";
    cout << desc;
    return 0;
  }

  if (vm.count("toy")) {
    cout << "toy enabled with seed " << seed << "\n";
    toy = kTRUE;
    //RooRandom::randomGenerator()->SetSeed(seed) ;
    // lumi should be intented as pb-1 and passed from outside
    scaleLumi = lumi / 45.0;  // 45 is the current lumi correspondent to the histogram.....
  }
  if (vm.count("fit")) {
    cout << "fit from data enabled \n";
    fitFromData = kTRUE;
  }

  if (!vm.count("toy") && !vm.count("fit")) {
    cerr << "Choose one beetween  fitting form data or with a toy MC \n";
    return 1;
  }

  if (toy == fitFromData) {
    cerr << "Choose if fit from data or with a toy MC \n";
    return 1;
  }

  if (vm.count("rebin")) {
    vector<int> v_rebin = vm["rebin"].as<vector<int> >();
    if (v_rebin.size() != 4) {
      cerr << " please provide 4 numbers in the given order:r_mutrk r mumuNotIso r_musa r _muhlt \n";
      return 1;
    }
    rebinZMuTk = v_rebin[0];
    rebinZMuMuNoIso = v_rebin[1];
    rebinZMuSa = v_rebin[2];
    rebinZMuMuHlt = v_rebin[3];
  }

  RooRealVar *mass = new RooRealVar("mass", "mass (GeV/c^{2})", fMin, fMax);
  TFile *root_file = new TFile(infile.c_str(), "read");
  int range = (int)(fMax - fMin);
  int numberOfBins = range / rebinZMuSa + range / rebinZMuTk + range / rebinZMuMuNoIso + 2 * range / rebinZMuMuHlt;
  // zmm histograms used for pdf
  TH1F *zmm = getHisto(root_file, "goodZToMuMuPlots/zMass", fMin, fMax, rebinZMuMu);
  // zmsta used for pdf
  TH1F *zmsta = getHisto(root_file, "zmumuSaMassHistogram/zMass", fMin, fMax, 1);  // histogramms to fit.....
  TH1F *zmmsta = getHisto(root_file, "goodZToMuMuOneStandAloneMuonPlots/zMass", fMin, fMax, rebinZMuSa / rebinZMuMu);
  TH1F *zmt = getHisto(root_file, "goodZToMuMuOneTrackPlots/zMass", fMin, fMax, rebinZMuTk / rebinZMuMu);
  TH1F *zmmNotIso = getHisto(root_file, "nonIsolatedZToMuMuPlots/zMass", fMin, fMax, rebinZMuMuNoIso / rebinZMuMu);
  TH1F *zmm1hlt = getHisto(root_file, "goodZToMuMu1HLTPlots/zMass", fMin, fMax, rebinZMuMuHlt / rebinZMuMu);
  TH1F *zmm2hlt = getHisto(root_file, "goodZToMuMu2HLTPlots/zMass", fMin, fMax, rebinZMuMuHlt / rebinZMuMu);

  // creating a pdf for Zmt

  RooHistPdf *ZmtPdf = createHistPdf("ZmtPdf", zmm, mass, rebinZMuTk / rebinZMuMu);
  // creating a pdf for Zmm not iso
  RooHistPdf *ZmmNoIsoPdf = createHistPdf("ZmmNoIsoPdf", zmm, mass, rebinZMuMuNoIso / rebinZMuMu);
  // creating a pdf for Zms from zmsta!!!
  RooHistPdf *ZmsPdf = createHistPdf("ZmsPdf", zmsta, mass, rebinZMuSa / rebinZMuMu);
  // creating a pdf for Zmmhlt
  RooHistPdf *ZmmHltPdf = createHistPdf("ZmmHltPdf", zmm, mass, rebinZMuMuHlt / rebinZMuMu);

  // creating the variable with random init values

  RooRealVar Yield("Yield", "Yield", 15000., 345., 3567890.);
  RooRealVar nbkg_mutrk("nbkg_mutrk", "background _mutrk fraction", 500, 0., 100000.);
  RooRealVar nbkg_mumuNotIso("nbkg_mumuNotIso", "background fraction", 500, 0., 100000.);
  RooRealVar nbkg_musa("nbkg_musa", "background fraction", 50, 0., 100000.);
  RooRealVar eff_tk("eff_tk", "signal _mutrk fraction", .99, 0.8, 1.0);
  RooRealVar eff_sa("eff_sa", "eff musta", 0.99, 0.8, 1.0);
  RooRealVar eff_iso("eff_iso", "eff mumuNotIso", .99, 0.8, 1.0);
  RooRealVar eff_hlt("eff_hlt", "eff 1hlt", 0.99, 0.8, 1.0);
  RooRealVar alpha("alpha", "coefficient alpha", -0.01, -1000, 1000.);
  RooRealVar a0("a0", "coefficient 0", 1, -1000., 1000.);
  RooRealVar a1("a1", "coefficient 1", -0.001, -1000, 1000.);
  RooRealVar a2("a2", "coefficient 2", 0.0, -1000., 1000.);
  RooRealVar beta("beta", "coefficient beta", -0.01, -1000, 1000.);
  RooRealVar b0("b0", "coefficient 0", 1, -1000., 1000.);
  RooRealVar b1("b1", "coefficient 1", -0.001, -1000, 1000.);
  RooRealVar b2("b2", "coefficient 2", 0.0, -1000., 1000.);
  RooRealVar gamma("gamma", "coefficient gamma", -0.01, -1000, 1000.);
  RooRealVar c0("c0", "coefficient 0", 1, -1000., 1000.);
  RooRealVar c1("c1", "coefficient 1", -0.001, -1000, 1000.);
  // fit parameters setted from datacard
  filebuf fb;
  fb.open("zMuMuRooFit.txt", ios::in);
  istream is(&fb);
  char line[1000];

  Yield.readFromStream(is.getline(line, 1000), kFALSE);
  nbkg_mutrk.readFromStream(is.getline(line, 1000), kFALSE);
  nbkg_mumuNotIso.readFromStream(is.getline(line, 1000), kFALSE);
  nbkg_musa.readFromStream(is.getline(line, 1000), kFALSE);
  eff_tk.readFromStream(is.getline(line, 1000), kFALSE);
  eff_sa.readFromStream(is.getline(line, 1000), kFALSE);
  eff_iso.readFromStream(is.getline(line, 1000), kFALSE);
  eff_hlt.readFromStream(is.getline(line, 1000), kFALSE);
  alpha.readFromStream(is.getline(line, 1000), kFALSE);
  a0.readFromStream(is.getline(line, 1000), kFALSE);
  a1.readFromStream(is.getline(line, 1000), kFALSE);
  a2.readFromStream(is.getline(line, 1000), kFALSE);
  beta.readFromStream(is.getline(line, 1000), kFALSE);
  b0.readFromStream(is.getline(line, 1000), kFALSE);
  b1.readFromStream(is.getline(line, 1000), kFALSE);
  b2.readFromStream(is.getline(line, 1000), kFALSE);
  gamma.readFromStream(is.getline(line, 1000), kFALSE);
  c0.readFromStream(is.getline(line, 1000), kFALSE);
  c1.readFromStream(is.getline(line, 1000), kFALSE);
  fb.close();

  // scaling to lumi if toy is enabled...
  if (vm.count("toy")) {
    Yield.setVal(scaleLumi * (Yield.getVal()));
    nbkg_mutrk.setVal(scaleLumi * (nbkg_mutrk.getVal()));
    nbkg_mumuNotIso.setVal(scaleLumi * (nbkg_mumuNotIso.getVal()));
  }

  //efficiency term

  //zMuMuEff1HLTTerm = _2 * (effTk ^ _2) *  (effSa ^ _2) * (effIso ^ _2) * effHLT * (_1 - effHLT);
  RooFormulaVar zMuMu1HLTEffTerm(
      "zMuMu1HLTEffTerm",
      "Yield * (2.* (eff_tk)^2 * (eff_sa)^2 * (eff_iso)^2 * eff_hlt *(1.- eff_hlt))",
      RooArgList(eff_tk,
                 eff_sa,
                 eff_iso,
                 eff_hlt,
                 Yield));  //zMuMuEff2HLTTerm = (effTk ^ _2) *  (effSa ^ _2) * (effIso ^ _2) * (effHLT ^ _2) ;
  RooFormulaVar zMuMu2HLTEffTerm("zMuMu2HLTEffTerm",
                                 "Yield * ((eff_tk)^2 * (eff_sa)^2 * (eff_iso)^2 * (eff_hlt)^2)",
                                 RooArgList(eff_tk, eff_sa, eff_iso, eff_hlt, Yield));
  //zMuMuNoIsoEffTerm = (effTk ^ _2) * (effSa ^ _2) * (_1 - (effIso ^ _2)) * (_1 - ((_1 - effHLT)^_2));
  RooFormulaVar zMuMuNoIsoEffTerm("zMuMuNoIsoEffTerm",
                                  "Yield * ((eff_tk)^2 * (eff_sa)^2 * (1.- (eff_iso)^2) * (1.- ((1.-eff_hlt)^2)))",
                                  RooArgList(eff_tk, eff_sa, eff_iso, eff_hlt, Yield));
  //zMuTkEffTerm = _2 * (effTk ^ _2) * effSa * (_1 - effSa) * (effIso ^ _2) * effHLT;
  RooFormulaVar zMuTkEffTerm("zMuTkEffTerm",
                             "Yield * (2. *(eff_tk)^2 * eff_sa * (1.-eff_sa)* (eff_iso)^2 * eff_hlt)",
                             RooArgList(eff_tk, eff_sa, eff_iso, eff_hlt, Yield));
  //zMuSaEffTerm = _2 * (effSa ^ _2) * effTk * (_1 - effTk) * (effIso ^ _2) * effHLT;
  RooFormulaVar zMuSaEffTerm("zMuSaEffTerm",
                             "Yield * (2. *(eff_sa)^2 * eff_tk * (1.-eff_tk)* (eff_iso)^2 * eff_hlt)",
                             RooArgList(eff_tk, eff_sa, eff_iso, eff_hlt, Yield));

  // creating model for the  fit
  // z mu track

  RooGenericPdf *bkg_mutrk = new RooGenericPdf("bkg_mutrk",
                                               "zmt bkg_model",
                                               "exp(alpha*mass) * ( a0 + a1 * mass + a2 * mass^2 )",
                                               RooArgSet(*mass, alpha, a0, a1, a2));
  // RooFormulaVar fracSigMutrk("fracSigMutrk", "@0 / (@0 + @1)", RooArgList(zMuTkEffTerm, nbkg_mutrk ));
  RooAddPdf *model_mutrk = new RooAddPdf(
      "model_mutrk", "model_mutrk", RooArgList(*ZmtPdf, *bkg_mutrk), RooArgList(zMuTkEffTerm, nbkg_mutrk));
  // z mu mu not Iso

  // creating background pdf for zmu mu not Iso
  RooGenericPdf *bkg_mumuNotIso = new RooGenericPdf("bkg_mumuNotIso",
                                                    "zmumuNotIso bkg_model",
                                                    "exp(beta * mass) * (b0 + b1 * mass + b2 * mass^2)",
                                                    RooArgSet(*mass, beta, b0, b1, b2));
  // RooFormulaVar fracSigMuMuNoIso("fracSigMuMuNoIso", "@0 / (@0 + @1)", RooArgList(zMuMuNoIsoEffTerm, nbkg_mumuNotIso ));
  RooAddPdf *model_mumuNotIso = new RooAddPdf("model_mumuNotIso",
                                              "model_mumuNotIso",
                                              RooArgList(*ZmmNoIsoPdf, *bkg_mumuNotIso),
                                              RooArgList(zMuMuNoIsoEffTerm, nbkg_mumuNotIso));
  // z mu sta

  // RooGenericPdf model_musta("model_musta",  " ZmsPdf * zMuSaEffTerm ", RooArgSet( *ZmsPdf, zMuSaEffTerm)) ;
  RooGenericPdf *bkg_musa = new RooGenericPdf(
      "bkg_musa", "zmusa bkg_model", "exp(gamma * mass) * (c0 + c1 * mass )", RooArgSet(*mass, gamma, c0, c1));
  // RooAddPdf * eZmsSig= new RooAddPdf("eZmsSig","eZmsSig",RooArgList(*,*bkg_mumuNotIso), RooArgList(zMuMuNoIsoEffTerm, nbkg_mumuNotIso));
  RooAddPdf *eZmsSig =
      new RooAddPdf("eZmsSig", "eZmsSig", RooArgList(*ZmsPdf, *bkg_musa), RooArgList(zMuSaEffTerm, nbkg_musa));

  //RooExtendPdf * eZmsSig= new RooExtendPdf("eZmsSig","extended signal p.d.f for zms ",*ZmsPdf,  zMuSaEffTerm ) ;

  // z mu mu HLT

  // count ZMuMu Yield
  double nZMuMu = 0.;
  double nZMuMu1HLT = 0.;
  double nZMuMu2HLT = 0.;
  unsigned int nBins = zmm->GetNbinsX();
  double xMin = zmm->GetXaxis()->GetXmin();
  double xMax = zmm->GetXaxis()->GetXmax();
  double deltaX = (xMax - xMin) / nBins;
  for (unsigned int i = 0; i < nBins; ++i) {
    double x = xMin + (i + .5) * deltaX;
    if (x > fMin && x < fMax) {
      nZMuMu += zmm->GetBinContent(i + 1);
      nZMuMu1HLT += zmm1hlt->GetBinContent(i + 1);
      nZMuMu2HLT += zmm2hlt->GetBinContent(i + 1);
    }
  }

  cout << ">>> count of ZMuMu yield in the range [" << fMin << ", " << fMax << "]: " << nZMuMu << endl;
  cout << ">>> count of ZMuMu (1HLT) yield in the range [" << fMin << ", " << fMax << "]: " << nZMuMu1HLT << endl;
  cout << ">>> count of ZMuMu (2HLT) yield in the range [" << fMin << ", " << fMax << "]: " << nZMuMu2HLT << endl;
  // we set eff_hlt
  //eff_hlt.setVal( 1. / (1. + (nZMuMu1HLT/ (2 * nZMuMu2HLT))) ) ;
  // creating the pdf for z mu mu 1hlt

  RooExtendPdf *eZmm1hltSig =
      new RooExtendPdf("eZmm1hltSig", "extended signal p.d.f for zmm 1hlt", *ZmmHltPdf, zMuMu1HLTEffTerm);
  // creating the pdf for z mu mu 2hlt
  RooExtendPdf *eZmm2hltSig =
      new RooExtendPdf("eZmm2hltSig", "extended signal p.d.f for zmm 2hlt", *ZmmHltPdf, zMuMu2HLTEffTerm);

  // getting the data if fit otherwise constructed the data for model if toy....

  RooDataHist *zmtMass, *zmmNotIsoMass, *zmsMass, *zmm1hltMass, *zmm2hltMass;

  if (toy) {
    zmtMass = genHistFromModelPdf("zmtMass", model_mutrk, mass, scaleLumi, range, rebinZMuTk, seed);
    zmmNotIsoMass =
        genHistFromModelPdf("zmmNotIsoMass", model_mumuNotIso, mass, scaleLumi, range, rebinZMuMuNoIso, seed);
    zmsMass = genHistFromModelPdf("zmsMass", eZmsSig, mass, scaleLumi, range, rebinZMuSa, seed);
    zmm1hltMass = genHistFromModelPdf("zmm1hltMass", eZmm1hltSig, mass, scaleLumi, range, rebinZMuMuHlt, seed);
    zmm2hltMass = genHistFromModelPdf("zmm2hltMass", eZmm2hltSig, mass, scaleLumi, range, rebinZMuMuHlt, seed);
  } else {  // if  fit from data....
    zmtMass = new RooDataHist("zmtMass", "good z mu track", RooArgList(*mass), zmt);
    zmmNotIsoMass = new RooDataHist("ZmmNotIso", "good z mu mu not isolated", RooArgList(*mass), zmmNotIso);
    zmsMass = new RooDataHist("zmsMass", "good z mu sta mass", RooArgList(*mass), zmmsta);
    zmm1hltMass = new RooDataHist("zmm1hltMass", "good Z mu mu 1hlt", RooArgList(*mass), zmm1hlt);
    zmm2hltMass = new RooDataHist("zmm2hltMass", "good Z mu mu 2hlt", RooArgList(*mass), zmm2hlt);
  }

  // creting the chi2s
  RooChi2Var *chi2_mutrk =
      new RooChi2Var("chi2_mutrk", "chi2_mutrk", *model_mutrk, *zmtMass, Extended(kTRUE), DataError(RooAbsData::SumW2));
  RooChi2Var *chi2_mumuNotIso = new RooChi2Var("chi2_mumuNotIso",
                                               "chi2_mumuNotIso",
                                               *model_mumuNotIso,
                                               *zmmNotIsoMass,
                                               Extended(kTRUE),
                                               DataError(RooAbsData::SumW2));

  RooChi2Var *chi2_musta =
      new RooChi2Var("chi2_musta", "chi2_musta", *eZmsSig, *zmsMass, Extended(kTRUE), DataError(RooAbsData::SumW2));
  // uncomment this line if you want to use logLik for mu sta
  // RooNLLVar *chi2_musta = new RooNLLVar("chi2_musta","chi2_musta",*eZmsSig, *zmsMass,  Extended(kTRUE), DataError(RooAbsData::SumW2) ) ;
  RooChi2Var *chi2_mu1hlt = new RooChi2Var(
      "chi2_mu1hlt", "chi2_mu1hlt", *eZmm1hltSig, *zmm1hltMass, Extended(kTRUE), DataError(RooAbsData::SumW2));
  RooChi2Var *chi2_mu2hlt = new RooChi2Var(
      "chi2_mu2hlt", "chi2_mu2hlt", *eZmm2hltSig, *zmm2hltMass, Extended(kTRUE), DataError(RooAbsData::SumW2));

  // adding the chi2
  RooAddition totChi2("totChi2",
                      "chi2_mutrk + chi2_mumuNotIso  + chi2_musta   + chi2_mu1hlt  +  chi2_mu2hlt ",
                      RooArgSet(*chi2_mutrk, *chi2_mumuNotIso, *chi2_musta, *chi2_mu1hlt, *chi2_mu2hlt));

  // printing out the model integral befor fitting
  double N_zMuMu1hlt = eZmm1hltSig->expectedEvents(*mass);
  double N_bkgTk = bkg_mutrk->expectedEvents(*mass);
  double N_bkgIso = bkg_mumuNotIso->expectedEvents(*mass);

  double N_zMuTk = zMuTkEffTerm.getVal();

  double e_hlt = eff_hlt.getVal();
  double e_tk = eff_tk.getVal();
  double e_sa = eff_sa.getVal();
  double e_iso = eff_iso.getVal();
  double Y_hlt = N_zMuMu1hlt / ((2. * (e_tk * e_tk) * (e_sa * e_sa) * (e_iso * e_iso) * e_hlt * (1. - e_hlt)));
  double Y_mutk = N_zMuTk / ((2. * (e_tk * e_tk) * e_sa * (1. - e_sa) * (e_iso * e_iso) * e_hlt));

  cout << "Yield prediction from mumu1hlt integral befor fitting: " << Y_hlt << endl;
  cout << "Yield prediction from mutk integral befor fitting: " << Y_mutk << endl;
  cout << "Bkg for mutk prediction from mutk integral after fitting: " << N_bkgTk << endl;
  cout << "Bkg for mumuNotIso prediction from mumuNotIso integral after fitting: " << N_bkgIso << endl;

  fit(totChi2, numberOfBins, outfile.c_str());
  N_zMuMu1hlt = eZmm1hltSig->expectedEvents(*mass);
  N_zMuTk = zMuTkEffTerm.getVal();
  double N_zMuMuNoIso = zMuMuNoIsoEffTerm.getVal();
  double N_Tk = model_mutrk->expectedEvents(*mass);
  double N_Iso = model_mumuNotIso->expectedEvents(*mass);
  e_hlt = eff_hlt.getVal();
  e_tk = eff_tk.getVal();
  e_sa = eff_sa.getVal();
  e_iso = eff_iso.getVal();
  Y_hlt = N_zMuMu1hlt / ((2. * (e_tk * e_tk) * (e_sa * e_sa) * (e_iso * e_iso) * e_hlt * (1. - e_hlt)));
  Y_mutk = N_zMuTk / ((2. * (e_tk * e_tk) * e_sa * (1. - e_sa) * (e_iso * e_iso) * e_hlt));

  cout << "Yield prediction from mumu1hlt integral after fitting: " << Y_hlt << endl;
  //cout << "Yield prediction from mutk integral after fitting: " <<  Y_mutk << endl;
  cout << "N + B  prediction from mutk integral after fitting: " << N_Tk << endl;
  cout << "zMuTkEffTerm " << N_zMuTk << endl;
  cout << "N + B  prediction from mumuNotIso integral after fitting: " << N_Iso << endl;
  cout << "zMuMuNoIsoEffTerm " << N_zMuMuNoIso << endl;

  cout << "chi2_mutrk:" << chi2_mutrk->getVal() << endl;
  cout << "chi2_mumuNotIso:" << chi2_mumuNotIso->getVal() << endl;
  cout << "chi2_musta:" << chi2_musta->getVal() << endl;
  cout << "chi2_mumu1hlt:" << chi2_mu1hlt->getVal() << endl;
  cout << "chi2_mumu2hlt:" << chi2_mu2hlt->getVal() << endl;

  //plotting
  RooPlot *massFrame_mutrk = mass->frame();
  RooPlot *massFrame_mumuNotIso = mass->frame();
  RooPlot *massFrame_musta = mass->frame();
  RooPlot *massFrame_mumu1hlt = mass->frame();
  RooPlot *massFrame_mumu2hlt = mass->frame();

  TCanvas *canv1 = new TCanvas("canvas");
  TCanvas *canv2 = new TCanvas("new_canvas");
  canv1->Divide(2, 3);
  canv1->cd(1);
  canv1->SetLogy(kTRUE);
  zmtMass->plotOn(massFrame_mutrk, LineColor(kBlue));
  model_mutrk->plotOn(massFrame_mutrk, LineColor(kRed));
  model_mutrk->plotOn(massFrame_mutrk, Components(*bkg_mutrk), LineColor(kGreen));
  massFrame_mutrk->SetTitle("Z -> #mu track");
  // massFrame_mutrk->GetYaxis()->SetLogScale();
  massFrame_mutrk->Draw();
  gPad->SetLogy(1);

  canv2->cd();
  canv2->SetLogy(kTRUE);
  massFrame_mutrk->Draw();
  canv2->SaveAs("LogZMuTk.eps");
  canv2->SetLogy(kFALSE);
  canv2->SaveAs("LinZMuTk.eps");

  canv1->cd(2);
  zmmNotIsoMass->plotOn(massFrame_mumuNotIso, LineColor(kBlue));
  model_mumuNotIso->plotOn(massFrame_mumuNotIso, LineColor(kRed));
  model_mumuNotIso->plotOn(massFrame_mumuNotIso, Components(*bkg_mumuNotIso), LineColor(kGreen));
  massFrame_mumuNotIso->SetTitle("Z -> #mu #mu not isolated");
  massFrame_mumuNotIso->Draw();
  gPad->SetLogy(1);

  canv2->cd();
  canv2->Clear();
  canv2->SetLogy(kTRUE);
  massFrame_mumuNotIso->Draw();
  canv2->SaveAs("LogZMuMuNotIso.eps");
  canv2->SetLogy(kFALSE);
  canv2->SaveAs("LinZMuMuNotIso.eps");

  canv1->cd(3);
  zmsMass->plotOn(massFrame_musta, LineColor(kBlue));
  eZmsSig->plotOn(massFrame_musta, Components(*bkg_musa), LineColor(kGreen));
  eZmsSig->plotOn(massFrame_musta, LineColor(kRed));
  massFrame_musta->SetTitle("Z -> #mu sta");
  massFrame_musta->Draw();

  canv2->cd();
  canv2->Clear();
  canv2->SetLogy(kTRUE);
  massFrame_musta->Draw();
  canv2->SaveAs("LogZMuSa.eps");
  canv2->SetLogy(kFALSE);
  canv2->SaveAs("LinZMuSa.eps");

  canv1->cd(4);
  zmm1hltMass->plotOn(massFrame_mumu1hlt, LineColor(kBlue));
  eZmm1hltSig->plotOn(massFrame_mumu1hlt, LineColor(kRed));
  massFrame_mumu1hlt->SetTitle("Z -> #mu #mu 1hlt");
  massFrame_mumu1hlt->Draw();
  canv2->cd();
  canv2->Clear();
  canv2->SetLogy(kTRUE);
  massFrame_mumu1hlt->Draw();
  canv2->SaveAs("LogZMuMu1Hlt.eps");
  canv2->SetLogy(kFALSE);
  canv2->SaveAs("LinZMuMu1Hlt.eps");

  canv1->cd(5);
  zmm2hltMass->plotOn(massFrame_mumu2hlt, LineColor(kBlue));
  eZmm2hltSig->plotOn(massFrame_mumu2hlt, LineColor(kRed));
  massFrame_mumu2hlt->SetTitle("Z -> #mu #mu 2hlt");
  massFrame_mumu2hlt->Draw();

  canv1->SaveAs("mass.eps");

  canv2->cd();
  canv2->Clear();
  canv2->SetLogy(kTRUE);
  massFrame_mumu2hlt->Draw();
  canv2->SaveAs("LogZMuMu2Hlt.eps");
  canv2->SetLogy(kFALSE);
  canv2->SaveAs("LinZMuMu2Hlt.eps");

  /* how to read the fit result in root
     TH1D h_Yield("h_Yield", "h_Yield", 100, 10000, 30000)
     for (int i =0: i < 100; i++){
     RooFitResult* r = gDirectory->Get(Form("toy_totChi2;%d)",i)
     //r->floatParsFinal().Print("s");
     // without s return a list,  can we get the number?
     RooFitResult* r = gDirectory->Get("toy_totChi2;1")
     // chi2
     r->minNll();
     //distamce form chi2.....
     //r->edm();
     // yield
     r->floatParsFinal()[0]->Print();
     //RooAbsReal * l = r->floatParsFinal()->first()
     RooAbsReal * y = r->floatParsFinal()->find("Yield");
     h_Yield->Fill(y->getVal());
     }

  */

  delete root_file;
  //delete out_root_file;
  return 0;
}


// __BEGIN_LICENSE__
//  Copyright (c) 2009-2013, United States Government as represented by the
//  Administrator of the National Aeronautics and Space Administration. All
//  rights reserved.
//
//  The NGT platform is 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.
// __END_LICENSE__

#include <vw/Core/FundamentalTypes.h>
#include <vw/Math/Vector.h>
#include <asp/IsisIO/PolyEquation.h>

#include <iomanip>
#include <vector>
#include <string>
#include <algorithm>

#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>

using namespace vw;
using namespace asp;

// Constructors
//---------------------------------------------
PolyEquation::PolyEquation ( int order ) {
  if ( order < 0 )
    vw_throw( ArgumentErr() << "PolyEquation: Polynomial order must be greater than zero." );
  if ( order > 254 )
    vw_throw( ArgumentErr() << "PolyEquation: Polynomial order must be less than 255" );
  m_x_coeff.set_size( order + 1 );
  m_y_coeff.set_size( order + 1 );
  m_z_coeff.set_size( order + 1 );
  for ( int i = 0; i < order+1; i++ )
    m_x_coeff[i] = m_y_coeff[i] = m_z_coeff[i] = 0;
  m_cached_time = -1;
  m_time_offset = 0;
  m_max_length = uint8(order)+1;
}
PolyEquation::PolyEquation( int order_x,
                            int order_y,
                            int order_z ) {
  if ( order_x < 0 || order_y < 0 || order_z < 0 )
    vw_throw( ArgumentErr() << "PolyEquation: Polynomial order must be greater than zero." );
  if ( order_x > 254 || order_y > 254 || order_z > 254 )
    vw_throw( ArgumentErr() << "PolyEquation: Polynomial order must be less than 255" );
  m_x_coeff.set_size(order_x+1);
  m_y_coeff.set_size(order_y+1);
  m_z_coeff.set_size(order_z+1);
  for ( unsigned i = 0; i < m_x_coeff.size(); i++ )
    m_x_coeff[i] = 0;
  for ( unsigned i = 0; i < m_y_coeff.size(); i++ )
    m_y_coeff[i] = 0;
  for ( unsigned i = 0; i < m_z_coeff.size(); i++ )
    m_z_coeff[i] = 0;
  m_cached_time = -1;
  m_time_offset = 0;
  m_max_length = uint8( std::max( order_x, std::max( order_y, order_z ) ) ) + 1;
}

// Update
//-----------------------------------------------
void PolyEquation::update( double t ) {
  m_cached_time = t;
  double delta_t = t-m_time_offset;
  Vector<double> powers( m_max_length );
  powers[0] = 1;
  for ( uint8 i = 1; i < m_max_length; i++ )
    powers[i] = powers[i-1]*delta_t;
  m_cached_output[0] = sum( elem_prod(m_x_coeff,
                                      subvector(powers,0,m_x_coeff.size())) );
  m_cached_output[1] = sum( elem_prod(m_y_coeff,
                                      subvector(powers,0,m_y_coeff.size())) );
  m_cached_output[2] = sum( elem_prod(m_z_coeff,
                                      subvector(powers,0,m_z_coeff.size())) );
}

// FileIO
//-----------------------------------------------
void PolyEquation::write( std::ofstream& f ) {
  for ( int i = 0; i < 3; i++ ) {
    Vector<double>* pointer;
    switch(i) {
    case 0:
      pointer = &m_x_coeff;
      break;
    case 1:
      pointer = &m_y_coeff;
      break;
    default:
    case 2:
      pointer = &m_z_coeff;
      break;
    }

    f << std::setprecision( 15 );
    for ( unsigned j = 0; j < (*pointer).size(); j++ )
      f << (*pointer)[j] << " ";
    f << "\n";
  }
}

void PolyEquation::read( std::ifstream& f ) {
  std::string buffer;
  std::vector<std::string> tokens;
  m_cached_time = -1;
  for ( int i = 0; i < 3; i++ ) {
    buffer = "";
    std::getline( f, buffer );
    boost::split( tokens, buffer, boost::is_any_of(" =\n") );

    // Cleaning out any tokens that are just ""
    for(std::vector<std::string>::iterator iter = tokens.begin();
        iter != tokens.end(); ++iter )
      if ( (*iter) == "" ) {
        iter = tokens.erase(iter);
        iter--;
      }

    Vector<double>* pointer;
    switch(i) {
    case 0:
      pointer = &m_x_coeff;
      break;
    case 1:
      pointer = &m_y_coeff;
      break;
    default:
    case 2:
      pointer = &m_z_coeff;
      break;
    }

    pointer->set_size( tokens.size() );
    for ( unsigned j = 0; j < tokens.size(); j++ )
      (*pointer)[j] = atof( tokens[j].c_str() );

  }
}

// Constant Access
//-----------------------------------------------
double& PolyEquation::operator[]( size_t n ) {
  m_cached_time = -1;
  if (n >= m_x_coeff.size() + m_y_coeff.size() + m_z_coeff.size())
    vw_throw(ArgumentErr() << "PolyEquation: invalid index.");
  if (n < m_x_coeff.size()) {
    return m_x_coeff[n];
  } else if (n < m_x_coeff.size() + m_y_coeff.size()) {
    return m_y_coeff[n - m_x_coeff.size()];
  } else {
    return m_z_coeff[n - m_x_coeff.size() - m_y_coeff.size()];
  }
}