/* ---------------------------------------------------------------------
 *
 * Copyright (C) 2000 - 2019 by the deal.II authors
 *
 * This file is part of the deal.II library.
 *
 * The deal.II library is free software; you can use it, redistribute
 * it, and/or modify it under the terms of the GNU Lesser General
 * Public License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * The full text of the license can be found in the file LICENSE.md at
 * the top level directory of deal.II.
 *
 * ---------------------------------------------------------------------

 *
 * Author: Wolfgang Bangerth, University of Texas at Austin, 2000, 2004
 *         Wolfgang Bangerth, Texas A&M University, 2016
 */

// @sect3{Include files}

// First the usual assortment of header files we have already used in previous
// example programs:
#include <deal.II/base/function.h>
#include <deal.II/base/logstream.h>
#include <deal.II/base/multithread_info.h>
#include <deal.II/base/quadrature_lib.h>
#include <deal.II/dofs/dof_accessor.h>
#include <deal.II/dofs/dof_handler.h>
#include <deal.II/dofs/dof_tools.h>
#include <deal.II/fe/fe_q.h>
#include <deal.II/fe/fe_system.h>
#include <deal.II/fe/fe_values.h>
#include <deal.II/grid/grid_generator.h>
#include <deal.II/grid/grid_refinement.h>
#include <deal.II/grid/tria.h>
#include <deal.II/grid/tria_accessor.h>
#include <deal.II/grid/tria_iterator.h>
#include <deal.II/lac/affine_constraints.h>
#include <deal.II/lac/dynamic_sparsity_pattern.h>
#include <deal.II/lac/full_matrix.h>
#include <deal.II/lac/sparsity_tools.h>
#include <deal.II/lac/vector.h>
#include <deal.II/numerics/data_out.h>
#include <deal.II/numerics/error_estimator.h>
#include <deal.II/numerics/matrix_tools.h>
#include <deal.II/numerics/vector_tools.h>

// And here come the things that we need particularly for this example program
// and that weren't in step-8. First, we replace the standard output
// std::cout by a new stream pcout which is used in
// parallel computations for generating output only on one of the MPI
// processes.
#include <deal.II/base/conditional_ostream.h>
// We are going to query the number of processes and the number of the present
// process by calling the respective functions in the Utilities::MPI
// namespace.
#include <deal.II/base/mpi.h>
// Then, we are going to replace all linear algebra components that involve
// the (global) linear system by classes that wrap interfaces similar to our
// own linear algebra classes around what PETSc offers (PETSc is a library
// written in C, and deal.II comes with wrapper classes that provide the PETSc
// functionality with an interface that is similar to the interface we already
// had for our own linear algebra classes). In particular, we need vectors and
// matrices that are distributed across several
// @ref GlossMPIProcess "processes" in MPI programs (and
// simply map to sequential, local vectors and matrices if there is only a
// single process, i.e., if you are running on only one machine, and without
// MPI support):
#include <deal.II/lac/petsc_sparse_matrix.h>
#include <deal.II/lac/petsc_vector.h>
// Then we also need interfaces for solvers and preconditioners that PETSc
// provides:
#include <deal.II/lac/petsc_precondition.h>
#include <deal.II/lac/petsc_solver.h>
// And in addition, we need some algorithms for partitioning our meshes so
// that they can be efficiently distributed across an MPI network. The
// partitioning algorithm is implemented in the GridTools
// namespace, and we need an additional include file for a function in
// DoFRenumbering that allows to sort the indices associated with
// degrees of freedom so that they are numbered according to the subdomain
// they are associated with:
#include <deal.II/dofs/dof_renumbering.h>
#include <deal.II/grid/grid_tools.h>

// And this is simply C++ again:
#include <fstream>
#include <iostream>

// The last step is as in all previous programs:
namespace Step17 {
using namespace dealii;

// @sect3{The ElasticProblem class template}

// The first real part of the program is the declaration of the main
// class.  As mentioned in the introduction, almost all of this has
// been copied verbatim from step-8, so we only comment on the few
// differences between the two tutorials.  There is one (cosmetic)
// change in that we let solve return a value, namely
// the number of iterations it took to converge, so that we can
// output this to the screen at the appropriate place.
template <int dim>
class ElasticProblem {
public:
    ElasticProblem();
    void run();

private:
    void setup_system();
    void assemble_system();
    unsigned int solve();
    void refine_grid();
    void output_results(const unsigned int cycle) const;

    // The first change is that we have to declare a variable that
    // indicates the @ref GlossMPICommunicator "MPI communicator" over
    // which we are supposed to distribute our computations.
    MPI_Comm mpi_communicator;

    // Then we have two variables that tell us where in the parallel
    // world we are. The first of the following variables,
    // n_mpi_processes, tells us how many MPI processes
    // there exist in total, while the second one,
    // this_mpi_process, indicates which is the number of
    // the present process within this space of processes (in MPI
    // language, this corresponds to the @ref GlossMPIRank "rank" of
    // the process). The latter will have a unique value for each
    // process between zero and (less than)
    // n_mpi_processes. If this program is run on a
    // single machine without MPI support, then their values are
    // 1 and 0, respectively.
    const unsigned int n_mpi_processes;
    const unsigned int this_mpi_process;

    // Next up is a stream-like variable pcout. It is, in essence,
    // just something we use for convenience: in a parallel program,
    // if each process outputs status information, then there quickly
    // is a lot of clutter. Rather, we would want to only have one
    // @ref GlossMPIProcess "process" output everything once, for
    // example the one with @ref GlossMPIRank "rank" zero. At the same
    // time, it seems silly to prefix every place where we
    // create output with an if (my_rank==0) condition.
    //
    // To make this simpler, the ConditionalOStream class does exactly
    // this under the hood: it acts as if it were a stream, but only
    // forwards to a real, underlying stream if a flag is set. By
    // setting this condition to this_mpi_process==0
    // (where this_mpi_process corresponds to the rank of
    // an MPI process), we make sure that output is only generated
    // from the first process and that we don't get the same lines of
    // output over and over again, once per process. Thus, we can use
    // pcout everywhere and in every process, but on all
    // but one process nothing will ever happen to the information
    // that is piped into the object via
    // operator<<.
    ConditionalOStream pcout;

    // The remainder of the list of member variables is fundamentally the
    // same as in step-8. However, we change the declarations of matrix
    // and vector types to use parallel PETSc objects instead. Note that
    // we do not use a separate sparsity pattern, since PETSc manages this
    // internally as part of its matrix data structures.
    Triangulation<dim> triangulation;
    FESystem<dim> fe;
    DoFHandler<dim> dof_handler;

    AffineConstraints<double> hanging_node_constraints;

    PETScWrappers::MPI::SparseMatrix system_matrix;

    PETScWrappers::MPI::Vector solution;
    PETScWrappers::MPI::Vector system_rhs;
};

// @sect3{Right hand side values}

// The following is taken from step-8 without change:
template <int dim>
class RightHandSide : public Function<dim> {
public:
    virtual void vector_value(const Point<dim> &p,
                              Vector<double> &values) const override
    {
        Assert(values.size() == dim, ExcDimensionMismatch(values.size(), dim));
        Assert(dim >= 2, ExcInternalError());

        Point<dim> point_1, point_2;
        point_1(0) = 0.5;
        point_2(0) = -0.5;

        if (((p - point_1).norm_square() < 0.2 * 0.2) ||
            ((p - point_2).norm_square() < 0.2 * 0.2))
            values(0) = 1;
        else
            values(0) = 0;

        if (p.square() < 0.2 * 0.2)
            values(1) = 1;
        else
            values(1) = 0;
    }

    virtual void vector_value_list(
        const std::vector<Point<dim>> &points,
        std::vector<Vector<double>> &value_list) const override
    {
        const unsigned int n_points = points.size();

        Assert(value_list.size() == n_points,
               ExcDimensionMismatch(value_list.size(), n_points));

        for (unsigned int p = 0; p < n_points; ++p)
            RightHandSide<dim>::vector_value(points[p], value_list[p]);
    }
};

// @sect3{The ElasticProblem class implementation}

// @sect4{ElasticProblem::ElasticProblem}

// The first step in the actual implementation is the constructor of
// the main class. Apart from initializing the same member variables
// that we already had in step-8, we here initialize the MPI
// communicator variable we shall use with the global MPI
// communicator linking all processes together (in more complex
// applications, one could here use a communicator object that only
// links a subset of all processes), and call the Utilities::MPI
// helper functions to determine the number of processes and where
// the present one fits into this picture. In addition, we make sure
// that output is only generated by the (globally) first process.
// We do so by passing the stream we want to output to
// (std::cout) and a true/false flag as arguments where
// the latter is determined by testing whether the process currently
// executing the constructor call is the first in the MPI universe.
template <int dim>
ElasticProblem<dim>::ElasticProblem()
    : mpi_communicator(MPI_COMM_WORLD),
      n_mpi_processes(Utilities::MPI::n_mpi_processes(mpi_communicator)),
      this_mpi_process(Utilities::MPI::this_mpi_process(mpi_communicator)),
      pcout(std::cout, (this_mpi_process == 0)),
      fe(FE_Q<dim>(1), dim),
      dof_handler(triangulation)
{}

// @sect4{ElasticProblem::setup_system}

// Next, the function in which we set up the various variables
// for the global linear system to be solved needs to be implemented.
//
// However, before we proceed with this, there is one thing to do for a
// parallel program: we need to determine which MPI process is
// responsible for each of the cells. Splitting cells among
// processes, commonly called "partitioning the mesh", is done by
// assigning a @ref GlossSubdomainId "subdomain id" to each cell. We
// do so by calling into the METIS library that does this in a very
// efficient way, trying to minimize the number of nodes on the
// interfaces between subdomains. Rather than trying to call METIS
// directly, we do this by calling the
// GridTools::partition_triangulation() function that does this at a
// much higher level of programming.
//
// @note As mentioned in the introduction, we could avoid this manual
//   partitioning step if we used the parallel::shared::Triangulation
//   class for the triangulation object instead (as we do in step-18).
//   That class does, in essence, everything a regular triangulation
//   does, but it then also automatically partitions the mesh after
//   every mesh creation or refinement operation.
//
// Following partitioning, we need to enumerate all degrees of
// freedom as usual.  However, we would like to enumerate the
// degrees of freedom in a way so that all degrees of freedom
// associated with cells in subdomain zero (which resides on process
// zero) come before all DoFs associated with cells on subdomain
// one, before those on cells on process two, and so on. We need
// this since we have to split the global vectors for right hand
// side and solution, as well as the matrix into contiguous chunks
// of rows that live on each of the processors, and we will want to
// do this in a way that requires minimal communication. This
// particular enumeration can be obtained by re-ordering degrees of
// freedom indices using DoFRenumbering::subdomain_wise().
//
// The final step of this initial setup is that we get ourselves an
// IndexSet that indicates the subset of the global number of unknowns
// this
// process is responsible for. (Note that a degree of freedom is not
// necessarily owned by the process that owns a cell just because
// the degree of freedom lives on this cell: some degrees of freedom
// live on interfaces between subdomains, and are consequently only owned by
// one of the processes adjacent to this interface.)
//
// Before we move on, let us recall a fact already discussed in the
// introduction: The triangulation we use here is replicated across
// all processes, and each process has a complete copy of the entire
// triangulation, with all cells. Partitioning only provides a way
// to identify which cells out of all each process "owns", but it
// knows everything about all of them. Likewise, the DoFHandler
// object knows everything about every cell, in particular the
// degrees of freedom that live on each cell, whether it is one that
// the current process owns or not. This can not scale to large
// problems because eventually just storing the entire mesh, and
// everything that is associated with it, on every process will
// become infeasible if the problem is large enough. On the other
// hand, if we split the triangulation into parts so that every
// process stores only those cells it "owns" but nothing else (or,
// at least a sufficiently small fraction of everything else), then
// we can solve large problems if only we throw a large enough
// number of MPI processes at them. This is what we are going to in
// step-40, for example, using the
// parallel::distributed::Triangulation class.  On the other hand,
// most of the rest of what we demonstrate in the current program
// will actually continue to work whether we have the entire
// triangulation available, or only a piece of it.
template <int dim>
void ElasticProblem<dim>::setup_system()
{
    GridTools::partition_triangulation(n_mpi_processes, triangulation);

    dof_handler.distribute_dofs(fe);
    DoFRenumbering::subdomain_wise(dof_handler);

    // We need to initialize the objects denoting hanging node constraints for
    // the present grid. As with the triangulation and DoFHandler objects, we
    // will simply store all constraints on each process; again, this
    // will not scale, but we show in step-40 how one can work around this by
    // only storing on each MPI process the constraints for degrees of freedom
    // that actually matter on this particular process.
    hanging_node_constraints.clear();
    DoFTools::make_hanging_node_constraints(dof_handler,
                                            hanging_node_constraints);
    hanging_node_constraints.close();

    // Now we create the sparsity pattern for the system matrix. Note that we
    // again compute and store all entries and not only the ones relevant
    // to this process (see step-18 or step-40 for a more efficient way to
    // handle this).
    DynamicSparsityPattern dsp(dof_handler.n_dofs(), dof_handler.n_dofs());
    DoFTools::make_sparsity_pattern(dof_handler, dsp, hanging_node_constraints,
                                    false);

    // Now we determine the set of locally owned DoFs and use that to
    // initialize parallel vectors and matrix. Since the matrix and vectors
    // need to work in parallel, we have to pass them an MPI communication
    // object, as well as information about the partitioning contained in the
    // IndexSet @p locally_owned_dofs.  The IndexSet contains information
    // about the global size (the total number of degrees of freedom)
    // and also what subset of rows is to be stored locally.  Note that the
    // system matrix needs that partitioning information for the rows and
    // columns. For square matrices, as it is the case here, the columns
    // should be partitioned in the same way as the rows, but in the case of
    // rectangular matrices one has to partition the columns in the same way
    // as vectors are partitioned with which the matrix is multiplied, while
    // rows have to partitioned in the same way as destination vectors of
    // matrix-vector multiplications:
    const std::vector<IndexSet> locally_owned_dofs_per_proc =
        DoFTools::locally_owned_dofs_per_subdomain(dof_handler);
    const IndexSet locally_owned_dofs =
        locally_owned_dofs_per_proc[this_mpi_process];

    system_matrix.reinit(locally_owned_dofs, locally_owned_dofs, dsp,
                         mpi_communicator);

    solution.reinit(locally_owned_dofs, mpi_communicator);
    system_rhs.reinit(locally_owned_dofs, mpi_communicator);
}

// @sect4{ElasticProblem::assemble_system}

// We now assemble the matrix and right hand side of the
// problem. There are some things worth mentioning before we go into
// detail. First, we will be assembling the system in parallel,
// i.e., each process will be responsible for assembling on cells
// that belong to this particular process. Note that the degrees of
// freedom are split in a way such that all DoFs in the interior of
// cells and between cells belonging to the same subdomain belong to
// the process that owns the cell. However, even then
// we sometimes need to assemble on a cell with a neighbor that
// belongs to a different process, and in these cases when we add up
// the local contributions into the global matrix or right hand side
// vector, we have to transfer these entries to the process that
// owns these elements. Fortunately, we don't have to do this by
// hand: PETSc does all this for us by caching these elements
// locally, and sending them to the other processes as necessary
// when we call the compress() functions on the matrix
// and vector at the end of this function.
//
// The second point is that once we have handed over matrix and
// vector contributions to PETSc, it is a) hard, and b) very
// inefficient to get them back for modifications. This is not only
// the fault of PETSc, it is also a consequence of the distributed
// nature of this program: if an entry resides on another processor,
// then it is necessarily expensive to get it. The consequence of
// this is that we should not try to first assemble the matrix and
// right hand side as if there were no hanging node constraints and
// boundary values, and then eliminate these in a second step
// (using, for example, AffineConstraints::condense()). Rather, we
// should try to eliminate hanging node constraints before handing
// these entries over to PETSc. This is easy: instead of copying
// elements by hand into the global matrix (as we do in step-4), we
// use the AffineConstraints::distribute_local_to_global() functions
// to take care of hanging nodes at the same time. We also already
// did this in step-6. The second step, elimination of boundary
// nodes, could also be done this way by putting the boundary values
// into the same AffineConstraints object as hanging nodes (see the
// way it is done in step-6, for example); however, it is not
// strictly necessary to do this here because eliminating boundary
// values can be done with only the data stored on each process
// itself, and consequently we use the approach used before in
// step-4, i.e., via MatrixTools::apply_boundary_values().
//
// All of this said, here is the actual implementation starting with
// the general setup of helper variables.  (Note that we still use
// the deal.II full matrix and vector types for the local systems as
// these are small and need not be shared across processes.)
template <int dim>
void ElasticProblem<dim>::assemble_system()
{
    QGauss<dim> quadrature_formula(fe.degree + 1);
    FEValues<dim> fe_values(fe, quadrature_formula,
                            update_values | update_gradients |
                                update_quadrature_points | update_JxW_values);

    const unsigned int dofs_per_cell = fe.dofs_per_cell;
    const unsigned int n_q_points = quadrature_formula.size();

    FullMatrix<double> cell_matrix(dofs_per_cell, dofs_per_cell);
    Vector<double> cell_rhs(dofs_per_cell);

    std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);

    std::vector<double> lambda_values(n_q_points);
    std::vector<double> mu_values(n_q_points);

    Functions::ConstantFunction<dim> lambda(1.), mu(1.);

    RightHandSide<dim> right_hand_side;
    std::vector<Vector<double>> rhs_values(n_q_points, Vector<double>(dim));

    // The next thing is the loop over all elements. Note that we do
    // not have to do all the work on every process: our job
    // here is only to assemble the system on cells that actually
    // belong to this MPI process, all other cells will be taken care
    // of by other processes. This is what the if-clause immediately
    // after the for-loop takes care of: it queries the subdomain
    // identifier of each cell, which is a number associated with each
    // cell that tells us about the owner process. In more generality,
    // the subdomain id is used to split a domain into several parts
    // (we do this above, at the beginning of
    // setup_system()), and which allows to identify
    // which subdomain a cell is living on. In this application, we
    // have each process handle exactly one subdomain, so we identify
    // the terms subdomain and MPI process.
    //
    // Apart from this, assembling the local system is relatively uneventful
    // if you have understood how this is done in step-8. As mentioned above,
    // distributing local contributions into the global matrix
    // and right hand sides also takes care of hanging node constraints in the
    // same way as is done in step-6.
    for (const auto &cell : dof_handler.active_cell_iterators())
        if (cell->subdomain_id() == this_mpi_process) {
            cell_matrix = 0;
            cell_rhs = 0;

            fe_values.reinit(cell);

            lambda.value_list(fe_values.get_quadrature_points(), lambda_values);
            mu.value_list(fe_values.get_quadrature_points(), mu_values);

            for (unsigned int i = 0; i < dofs_per_cell; ++i) {
                const unsigned int component_i =
                    fe.system_to_component_index(i).first;

                for (unsigned int j = 0; j < dofs_per_cell; ++j) {
                    const unsigned int component_j =
                        fe.system_to_component_index(j).first;

                    for (unsigned int q_point = 0; q_point < n_q_points;
                         ++q_point) {
                        cell_matrix(i, j) +=
                            ((fe_values.shape_grad(i, q_point)[component_i] *
                              fe_values.shape_grad(j, q_point)[component_j] *
                              lambda_values[q_point]) +
                             (fe_values.shape_grad(i, q_point)[component_j] *
                              fe_values.shape_grad(j, q_point)[component_i] *
                              mu_values[q_point]) +
                             ((component_i == component_j)
                                  ? (fe_values.shape_grad(i, q_point) *
                                     fe_values.shape_grad(j, q_point) *
                                     mu_values[q_point])
                                  : 0)) *
                            fe_values.JxW(q_point);
                    }
                }
            }

            right_hand_side.vector_value_list(fe_values.get_quadrature_points(),
                                              rhs_values);
            for (unsigned int i = 0; i < dofs_per_cell; ++i) {
                const unsigned int component_i =
                    fe.system_to_component_index(i).first;

                for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
                    cell_rhs(i) += fe_values.shape_value(i, q_point) *
                                   rhs_values[q_point](component_i) *
                                   fe_values.JxW(q_point);
            }

            cell->get_dof_indices(local_dof_indices);
            hanging_node_constraints.distribute_local_to_global(
                cell_matrix, cell_rhs, local_dof_indices, system_matrix,
                system_rhs);
        }

    // The next step is to "compress" the vector and the system matrix. This
    // means that each process sends the additions that were made to those
    // entries of the matrix and vector that the process did not own itself to
    // the process that owns them. After receiving these additions from other
    // processes, each process then adds them to the values it already
    // has. These additions are combining the integral contributions of shape
    // functions living on several cells just as in a serial computation, with
    // the difference that the cells are assigned to different processes.
    system_matrix.compress(VectorOperation::add);
    system_rhs.compress(VectorOperation::add);

    // The global matrix and right hand side vectors have now been
    // formed. We still have to apply boundary values, in the same way as we
    // did, for example, in step-3, step-4, and a number of other programs.
    //
    // The last argument to the call to
    // MatrixTools::apply_boundary_values() below allows for some
    // optimizations. It controls whether we should also delete
    // entries (i.e., set them to zero) in the matrix columns
    // corresponding to boundary nodes, or to keep them (and passing
    // true means: yes, do eliminate the columns). If we
    // do eliminate columns, then the resulting matrix will be
    // symmetric again if it was before; if we don't, then it
    // won't. The solution of the resulting system should be the same,
    // though. The only reason why we may want to make the system
    // symmetric again is that we would like to use the CG method,
    // which only works with symmetric matrices. The reason why we may
    // not want to make the matrix symmetric is because this
    // would require us to write into column entries that actually
    // reside on other processes, i.e., it involves communicating
    // data. This is always expensive.
    //
    // Experience tells us that CG also works (and works almost as
    // well) if we don't remove the columns associated with boundary
    // nodes, which can be explained by the special structure of this
    // particular non-symmetry. To avoid the expense of communication,
    // we therefore do not eliminate the entries in the affected
    // columns.
    std::map<types::global_dof_index, double> boundary_values;
    VectorTools::interpolate_boundary_values(
        dof_handler, 0, Functions::ZeroFunction<dim>(dim), boundary_values);
    MatrixTools::apply_boundary_values(boundary_values, system_matrix, solution,
                                       system_rhs, false);
}

// @sect4{ElasticProblem::solve}

// Having assembled the linear system, we next need to solve
// it. PETSc offers a variety of sequential and parallel solvers,
// for which we have written wrappers that have almost the same
// interface as is used for the deal.II solvers used in all previous
// example programs. The following code should therefore look rather
// familiar.
//
// At the top of the function, we set up a convergence monitor, and
// assign it the accuracy to which we would like to solve the linear
// system. Next, we create an actual solver object using PETSc's CG
// solver which also works with parallel (distributed) vectors and
// matrices. And finally a preconditioner; we choose to use a block
// Jacobi preconditioner which works by computing an incomplete LU
// decomposition on each diagonal block of the matrix.  (In other
// words, each MPI process computes an ILU from the rows it stores
// by throwing away columns that correspond to row indices not
// stored locally; this yields a square matrix block from which we
// can compute an ILU. That means that if you run the program with
// only one process, then you will use an ILU(0) as a
// preconditioner, while if it is run on many processes, then we
// will have a number of blocks on the diagonal and the
// preconditioner is the ILU(0) of each of these blocks. In the
// extreme case of one degree of freedom per processor, this
// preconditioner is then simply a Jacobi preconditioner since the
// diagonal matrix blocks consist of only a single entry. Such a
// preconditioner is relatively easy to compute because it does not
// require any kind of communication between processors, but it is
// in general not very efficient for large numbers of processors.)
//
// Following this kind of setup, we then solve the linear system:
template <int dim>
unsigned int ElasticProblem<dim>::solve()
{
    SolverControl solver_control(solution.size(), 1e-8 * system_rhs.l2_norm());
    PETScWrappers::SolverCG cg(solver_control, mpi_communicator);

    PETScWrappers::PreconditionBlockJacobi preconditioner(system_matrix);

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

    // The next step is to distribute hanging node constraints. This is a
    // little tricky, since to fill in the value of a constrained node you
    // need access to the values of the nodes to which it is constrained (for
    // example, for a Q1 element in 2d, we need access to the two nodes on the
    // big side of a hanging node face, to compute the value of the
    // constrained node in the middle).
    //
    // The problem is that we have built our vectors (in
    // setup_system()) in such a way that every process
    // is responsible for storing only those elements of the solution
    // vector that correspond to the degrees of freedom this process
    // "owns". There are, however, cases where in order to compute the
    // value of the vector entry for a constrained degree of freedom
    // on one process, we need to access vector entries that are
    // stored on other processes.  PETSc (and, for that matter, the
    // MPI model on which it is built) does not allow to simply query
    // vector entries stored on other processes, so what we do here is
    // to get a copy of the "distributed" vector where we store all
    // elements locally. This is simple, since the deal.II wrappers
    // have a conversion constructor for the deal.II Vector
    // class. (This conversion of course requires communication, but
    // in essence every process only needs to send its data to every
    // other process once in bulk, rather than having to respond to
    // queries for individual elements):
    Vector<double> localized_solution(solution);

    // Of course, as in previous discussions, it is clear that such a
    // step cannot scale very far if you wanted to solve large
    // problems on large numbers of processes, because every process
    // now stores all elements of the solution vector. (We will
    // show how to do this better in step-40.)  On the other hand,
    // distributing hanging node constraints is simple on this local
    // copy, using the usual function
    // AffineConstraints::distributed(). In particular, we can compute
    // the values of all constrained degrees of freedom,
    // whether the current process owns them or not:
    hanging_node_constraints.distribute(localized_solution);

    // Then transfer everything back into the global vector. The
    // following operation copies those elements of the localized
    // solution that we store locally in the distributed solution, and
    // does not touch the other ones. Since we do the same operation
    // on all processors, we end up with a distributed vector (i.e., a
    // vector that on every process only stores the vector entries
    // corresponding to degrees of freedom that are owned by this
    // process) that has all the constrained nodes fixed.
    //
    // We end the function by returning the number of iterations it
    // took to converge, to allow for some output.
    solution = localized_solution;

    return solver_control.last_step();
}

// @sect4{ElasticProblem::refine_grid}

// Using some kind of refinement indicator, the mesh can be
// refined. The problem is basically the same as with distributing
// hanging node constraints: in order to compute the error indicator
// (even if we were just interested in the indicator on the cells
// the current process owns), we need access to more elements of the
// solution vector than just those the current processor stores. To
// make this happen, we do essentially what we did in
// solve() already, namely get a complete copy
// of the solution vector onto every process, and use that to
// compute. This is in itself expensive as explained above and it
// is particular unnecessary since we had just created and then
// destroyed such a vector in solve(), but efficiency
// is not the point of this program and so let us opt for a design
// in which every function is as self-contained as possible.
//
// Once we have such a "localized" vector that contains all
// elements of the solution vector, we can compute the indicators
// for the cells that belong to the present process. In fact, we
// could of course compute all refinement indicators since
// our Triangulation and DoFHandler objects store information about
// all cells, and since we have a complete copy of the solution
// vector. But in the interest in showing how to operate in
// %parallel, let us demonstrate how one would operate if one were
// to only compute some error indicators and then exchange
// the remaining ones with the other processes. (Ultimately, each
// process needs a complete set of refinement indicators because
// every process needs to refine their mesh, and needs to refine it
// in exactly the same way as all of the other processes.)
//
// So, to do all of this, we need to:
// - First, get a local copy of the distributed solution vector.
// - Second, create a vector to store the refinement indicators.
// - Third, let the KellyErrorEstimator compute refinement
//   indicators for all cells belonging to the present
//   subdomain/process. The last argument of the call indicates
//   which subdomain we are interested in. The three arguments
//   before it are various other default arguments that one usually
//   does not need (and does not state values for, but rather uses the
//   defaults), but which we have to state here explicitly since we
//   want to modify the value of a following argument (i.e., the one
//   indicating the subdomain).
template <int dim>
void ElasticProblem<dim>::refine_grid()
{
    const Vector<double> localized_solution(solution);

    Vector<float> local_error_per_cell(triangulation.n_active_cells());
    KellyErrorEstimator<dim>::estimate(
        dof_handler, QGauss<dim - 1>(fe.degree + 1), {}, localized_solution,
        local_error_per_cell, ComponentMask(), nullptr,
        MultithreadInfo::n_threads(), this_mpi_process);

    // Now all processes have computed error indicators for their own
    // cells and stored them in the respective elements of the
    // local_error_per_cell vector. The elements of this
    // vector for cells not owned by the present process are
    // zero. However, since all processes have a copy of the entire
    // triangulation and need to keep these copies in sync, they need
    // the values of refinement indicators for all cells of the
    // triangulation. Thus, we need to distribute our results. We do
    // this by creating a distributed vector where each process has
    // its share and sets the elements it has computed. Consequently,
    // when you view this vector as one that lives across all
    // processes, then every element of this vector has been set
    // once. We can then assign this parallel vector to a local,
    // non-parallel vector on each process, making all error
    // indicators available on every process.
    //
    // So in the first step, we need to set up a parallel vector. For
    // simplicity, every process will own a chunk with as many
    // elements as this process owns cells, so that the first chunk of
    // elements is stored with process zero, the next chunk with
    // process one, and so on. It is important to remark, however,
    // that these elements are not necessarily the ones we will write
    // to. This is a consequence of the order in which cells are arranged,
    // i.e., the order in which the elements of the vector correspond
    // to cells is not ordered according to the subdomain these cells
    // belong to. In other words, if on this process we compute
    // indicators for cells of a certain subdomain, we may write the
    // results to more or less random elements of the distributed
    // vector; in particular, they may not necessarily lie within the
    // chunk of vector we own on the present process. They will
    // subsequently have to be copied into another process' memory
    // space, an operation that PETSc does for us when we call the
    // compress() function. This inefficiency could be
    // avoided with some more code, but we refrain from it since it is
    // not a major factor in the program's total runtime.
    //
    // So here is how we do it: count how many cells belong to this
    // process, set up a distributed vector with that many elements to
    // be stored locally, copy over the elements we computed
    // locally, and finally compress the result. In fact, we really only copy
    // the elements that are nonzero, so we may miss a few that we
    // computed to zero, but this won't hurt since the original values
    // of the vector are zero anyway.
    const unsigned int n_local_cells =
        GridTools::count_cells_with_subdomain_association(triangulation,
                                                          this_mpi_process);
    PETScWrappers::MPI::Vector distributed_all_errors(
        mpi_communicator, triangulation.n_active_cells(), n_local_cells);

    for (unsigned int i = 0; i < local_error_per_cell.size(); ++i)
        if (local_error_per_cell(i) != 0)
            distributed_all_errors(i) = local_error_per_cell(i);
    distributed_all_errors.compress(VectorOperation::insert);

    // So now we have this distributed vector that contains the
    // refinement indicators for all cells. To use it, we need to
    // obtain a local copy and then use it to mark cells for
    // refinement or coarsening, and actually do the refinement and
    // coarsening. It is important to recognize that every
    // process does this to its own copy of the triangulation, and
    // does it in exactly the same way.
    const Vector<float> localized_all_errors(distributed_all_errors);

    GridRefinement::refine_and_coarsen_fixed_number(
        triangulation, localized_all_errors, 0.3, 0.03);
    triangulation.execute_coarsening_and_refinement();
}

// @sect4{ElasticProblem::output_results}

// The final function of significant interest is the one that
// creates graphical output. This works the same way as in step-8,
// with two small differences. Before discussing these, let us state
// the general philosophy this function will work: we intend for all
// of the data to be generated on a single process, and subsequently
// written to a file. This is, as many other parts of this program
// already discussed, not something that will scale. Previously, we
// had argued that we will get into trouble with triangulations,
// DoFHandlers, and copies of the solution vector where every
// process has to store all of the data, and that there will come to
// be a point where each process simply doesn't have enough memory
// to store that much data. Here, the situation is different: it's
// not only the memory, but also the run time that's a problem. If
// one process is responsible for processing all of the data
// while all of the other processes do nothing, then this one
// function will eventually come to dominate the overall run time of
// the program.  In particular, the time this function takes is
// going to be proportional to the overall size of the problem
// (counted in the number of cells, or the number of degrees of
// freedom), independent of the number of processes we throw at it.
//
// Such situations need to be avoided, and we will show in step-18
// and step-40 how to address this issue. For the current problem,
// the solution is to have each process generate output data only
// for its own local cells, and write them to separate files, one
// file per process. This is how step-18 operates. Alternatively,
// one could simply leave everything in a set of independent files
// and let the visualization software read all of them (possibly
// also using multiple processors) and create a single visualization
// out of all of them; this is the path step-40, step-32, and all
// other parallel programs developed later on take.
//
// More specifically for the current function, all processes call
// this function, but not all of them need to do the work associated
// with generating output. In fact, they shouldn't, since we would
// try to write to the same file multiple times at once. So we let
// only the first process do this, and all the other ones idle
// around during this time (or start their work for the next
// iteration, or simply yield their CPUs to other jobs that happen
// to run at the same time). The second thing is that we not only
// output the solution vector, but also a vector that indicates
// which subdomain each cell belongs to. This will make for some
// nice pictures of partitioned domains.
//
// To implement this, process zero needs a complete set of solution
// components in a local vector. Just as with the previous function,
// the efficient way to do this would be to re-use the vector
// already created in the solve() function, but to keep
// things more self-contained, we simply re-create one here from the
// distributed solution vector.
//
// An important thing to realize is that we do this localization operation
// on all processes, not only the one that actually needs the data. This
// can't be avoided, however, with the simplified communication model of MPI
// we use for vectors in this tutorial program: MPI does not have a way to
// query data on another process, both sides have to initiate a
// communication at the same time. So even though most of the processes do
// not need the localized solution, we have to place the statement
// converting the distributed into a localized vector so that all processes
// execute it.
//
// (Part of this work could in fact be avoided. What we do is
// send the local parts of all processes to all other processes. What we
// would really need to do is to initiate an operation on all processes
// where each process simply sends its local chunk of data to process
// zero, since this is the only one that actually needs it, i.e., we need
// something like a gather operation. PETSc can do this, but for
// simplicity's sake we don't attempt to make use of this here. We don't,
// since what we do is not very expensive in the grand scheme of things:
// it is one vector communication among all processes, which has to be
// compared to the number of communications we have to do when solving the
// linear system, setting up the block-ILU for the preconditioner, and
// other operations.)
template <int dim>
void ElasticProblem<dim>::output_results(const unsigned int cycle) const
{
    const Vector<double> localized_solution(solution);

    // This being done, process zero goes ahead with setting up the
    // output file as in step-8, and attaching the (localized)
    // solution vector to the output object.
    if (this_mpi_process == 0) {
        std::ofstream output("solution-" + std::to_string(cycle) + ".vtk");

        DataOut<dim> data_out;
        data_out.attach_dof_handler(dof_handler);

        std::vector<std::string> solution_names;
        switch (dim) {
        case 1:
            solution_names.emplace_back("displacement");
            break;
        case 2:
            solution_names.emplace_back("x_displacement");
            solution_names.emplace_back("y_displacement");
            break;
        case 3:
            solution_names.emplace_back("x_displacement");
            solution_names.emplace_back("y_displacement");
            solution_names.emplace_back("z_displacement");
            break;
        default:
            Assert(false, ExcInternalError());
        }

        data_out.add_data_vector(localized_solution, solution_names);

        // The only other thing we do here is that we also output one
        // value per cell indicating which subdomain (i.e., MPI
        // process) it belongs to. This requires some conversion work,
        // since the data the library provides us with is not the one
        // the output class expects, but this is not difficult. First,
        // set up a vector of integers, one per cell, that is then
        // filled by the subdomain id of each cell.
        //
        // The elements of this vector are then converted to a
        // floating point vector in a second step, and this vector is
        // added to the DataOut object, which then goes off creating
        // output in VTK format:
        std::vector<unsigned int> partition_int(triangulation.n_active_cells());
        GridTools::get_subdomain_association(triangulation, partition_int);

        const Vector<double> partitioning(partition_int.begin(),
                                          partition_int.end());

        data_out.add_data_vector(partitioning, "partitioning");

        data_out.build_patches();
        data_out.write_vtk(output);
    }
}

// @sect4{ElasticProblem::run}

// Lastly, here is the driver function. It is almost completely
// unchanged from step-8, with the exception that we replace
// std::cout by the pcout stream. Apart
// from this, the only other cosmetic change is that we output how
// many degrees of freedom there are per process, and how many
// iterations it took for the linear solver to converge:
template <int dim>
void ElasticProblem<dim>::run()
{
    for (unsigned int cycle = 0; cycle < 10; ++cycle) {
        pcout << "Cycle " << cycle << ':' << std::endl;

        if (cycle == 0) {
            GridGenerator::hyper_cube(triangulation, -1, 1);
            triangulation.refine_global(3);
        } else
            refine_grid();

        pcout << "   Number of active cells:       "
              << triangulation.n_active_cells() << std::endl;

        setup_system();

        pcout << "   Number of degrees of freedom: " << dof_handler.n_dofs()
              << " (by partition:";
        for (unsigned int p = 0; p < n_mpi_processes; ++p)
            pcout << (p == 0 ? ' ' : '+')
                  << (DoFTools::count_dofs_with_subdomain_association(
                         dof_handler, p));
        pcout << ")" << std::endl;

        assemble_system();
        const unsigned int n_iterations = solve();

        pcout << "   Solver converged in " << n_iterations << " iterations."
              << std::endl;

        output_results(cycle);
    }
}
}  // namespace Step17

// @sect3{The main function}

// The main() works the same way as most of the main
// functions in the other example programs, i.e., it delegates work to
// the run function of a master object, and only wraps
// everything into some code to catch exceptions:
int main(int argc, char **argv)
{
    try {
        using namespace dealii;
        using namespace Step17;

        // Here is the only real difference: MPI and PETSc both require that we
        // initialize these libraries at the beginning of the program, and
        // un-initialize them at the end. The class MPI_InitFinalize takes care
        // of all of that. The trailing argument `1` means that we do want to
        // run each MPI process with a single thread, a prerequisite with the
        // PETSc parallel linear algebra.
        Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv, 1);

        ElasticProblem<2> elastic_problem;
        elastic_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 <networkit/graph/Graph.hpp>
#include <networkit/io/EdgeListReader.hpp>
#include <networkit/centrality/DegreeCentrality.hpp>
#include <networkit/graph/BFS.hpp>
#include <boost/program_options.hpp>
#include <networkit/centrality/CoreDecomposition.hpp>
#include "../include/decompositions/hDegreeCentrality.hpp"
#include "../include/decompositions/DistanceGeneralizedCoreDecomposition.hpp"
#include "../include/decompositions/KPeakDecomposition.hpp"
#include "../include/decompositions/NeighborCoreness.hpp"
#include "../include/decompositions/EdgeSupportCentrality.hpp" //<-----to remove---->
#include "../include/decompositions/TrussDecomposition.hpp"
#include "utility.hpp"
#include <iostream>
#include <memory>
#include <string>
#include <fstream>
#include <algorithm>
#include <iterator>
#include <type_traits>

namespace po = boost::program_options;

// Define custom validator for
// specifying the algorithm from
// command line
struct deco_algorithm {
public:
    deco_algorithm(const std::string &n): name(n){}
    std::string name;
};

//custom  validator for the algorithm field
void validate(boost::any& v,
            const std::vector<std::string>& values,
            deco_algorithm*, int){

    static std::vector<std::string> available_algorithms = {
        "kcore",
        "kpeak",
        "ktruss",
        "neighcoreness",
        "dgcore"
    };

    // Make sure no previous assignment to 'a' was made.
    po::validators::check_first_occurrence(v);

    // Extract the first string from 'values'. If there is more than
    // one string, it's an error, and exception will be thrown.
    std::string s = po::validators::get_single_string(values);

    // convert to lower case
    std::for_each(s.begin(), s.end(), [](char &c) {c = ::tolower(c);});

    //check if the given algo arg correspond to an available decomposition
    if(std::find(available_algorithms.begin(),
                 available_algorithms.end(),s)==available_algorithms.end())
            throw po::validation_error(po::validation_error::invalid_option_value);

    v = boost::any(deco_algorithm(s));
}

int main(int argc, char const *argv[])
{
    try{
        //program options
        po::options_description desc("Allowed Options");
        desc.add_options()
            ("help", "produce help message")
            ("input,in", po::value<std::string>(), "input graph")
            ("output,out", po::value<std::string>(), "ouput file")
            ("algorithm,a", po::value<deco_algorithm>(),
            "decomposition algorithm. Available options: \n\t [*] kcore \n\t [*] kpeak \n\t [*] ktruss \n\t [*] neighcoreness \n\t [*] dgcore");

        // define positional arguments
        po::positional_options_description pos_args;
        pos_args.add("input", 1);
        pos_args.add("output", 1);
        pos_args.add("algorithm", 1);

        //distance generalized core decomposition algorithm options
        po::options_description dgc_opt("Dist. Gen. Core Deco. options");
        dgc_opt.add_options()("distance,h", po::value<int>()->default_value(1), "neighborhood distance");

        //neighbor coreness decomposition algorithm options
        po::options_description neigh_opt("Neighbor Coreness Deco. options");
        neigh_opt.add_options()("depth,d", po::value<int>()->default_value(1), "neighbors depth");

        //graph input reader option
        po::options_description reader_options("Graph Reader Options");
        reader_options.add_options()
            ("first-node, fn", po::value<int>()->default_value(0), "first node index")
            ("separator, sep", po::value<char>()->default_value(' '), "separator");

        //add groups to the general options description
        desc.add(reader_options).add(dgc_opt).add(neigh_opt);

        // read command line arguments
        po::variables_map vm;
        po::store(po::command_line_parser(argc, argv)
            .options(desc)
            .positional(pos_args)
            .run(), vm);
        po::notify(vm);

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

        //read the input graph
        auto input_dataset = vm["input"].as<std::string>();
        auto first_node = vm["first-node"].as<int>();
        auto separator = vm["separator"].as<char>();

        std::cout << "Start Reading Graph" << std::endl;
        NetworKit::EdgeListReader reader(separator, first_node, "#", true, true);
        NetworKit::Graph g =  reader.read(input_dataset);
        std::cout << g.numberOfNodes() <<  " is Directed: "<< g.isDirected() <<std::endl;

        //create the algorithm
        std::unique_ptr<NetworKit::Centrality> algo_ptr;
        auto deco_algo = vm["algorithm"].as<deco_algorithm>().name;
        if(deco_algo=="kcore"){
            algo_ptr = std::make_unique<NetworKit::CoreDecomposition>(g, false, true, false, NetworKit::CoreDecomposition::Direction::Out);
        }else if (deco_algo=="dgcore"){
            int h = vm["distance"].as<int>();
            algo_ptr = std::make_unique<NetworKit::DistanceGeneralizedCoreDecomposition>(g, h);
        }else if (deco_algo=="kpeak"){
            algo_ptr = std::make_unique<NetworKit::KPeakDecomposition>(g);
        }else if (deco_algo=="neighcoreness"){
            int depth = vm["depth"].as<int>();
            algo_ptr = std::make_unique<NetworKit::NeighborCoreness>(g, depth);
        }else if (deco_algo=="ktruss"){
          g.indexEdges(true); //create edge indexes
          algo_ptr = std::make_unique<NetworKit::TrussDecomposition>(g);
          //          algo_ptr = std::make_unique<NetworKit::EdgeSupportCentrality>(g);
        }

        //execute the algorithm
        int time = Utility::timed_run([&](){
            algo_ptr->run();
        });
        std::cout << "Time = " <<  time << "[s]" << std::endl;

        //save results to the output file
        std::string output_file = vm["output"].as<std::string>();
        Utility::saveResult(g,*algo_ptr, output_file);

    }catch(std::exception &e){
        std::cout << e.what() << "\n";
        return 1;
    }
    return 0;
}


/****************************************************************************
 * Copyright (c) 2017-2021 by the ArborX authors                            *
 * All rights reserved.                                                     *
 *                                                                          *
 * This file is part of the ArborX library. ArborX is                       *
 * distributed under a BSD 3-clause license. For the licensing terms see    *
 * the LICENSE file in the top-level directory.                             *
 *                                                                          *
 * SPDX-License-Identifier: BSD-3-Clause                                    *
 ****************************************************************************/
#include "ArborX_EnableDeviceTypes.hpp" // ARBORX_DEVICE_TYPES
#include <ArborX_DBSCAN.hpp>
#include <ArborX_DBSCANVerification.hpp>

#include "BoostTest_CUDA_clang_workarounds.hpp"
#include <boost/test/unit_test.hpp>

template <typename View>
struct HiddenView
{
  View _view;
};
template <typename View>
struct ArborX::AccessTraits<HiddenView<View>, ArborX::PrimitivesTag>
{
  using Data = HiddenView<View>;
  static KOKKOS_FUNCTION std::size_t size(Data const &data)
  {
    return data._view.extent(0);
  }
  static KOKKOS_FUNCTION typename View::value_type const &get(Data const &data,
                                                              std::size_t i)
  {
    return data._view(i);
  }
  using memory_space = typename View::memory_space;
};

BOOST_AUTO_TEST_SUITE(DBSCAN)

template <typename DeviceType, typename T>
auto buildView(std::vector<T> const &v)
{
  Kokkos::View<T *, DeviceType> view("Testing::v", v.size());
  Kokkos::deep_copy(view, Kokkos::View<T const *, Kokkos::HostSpace,
                                       Kokkos::MemoryTraits<Kokkos::Unmanaged>>(
                              v.data(), v.size()));
  return view;
}

BOOST_AUTO_TEST_CASE_TEMPLATE(dbscan_verifier, DeviceType, ARBORX_DEVICE_TYPES)
{
  using ExecutionSpace = typename DeviceType::execution_space;
  using ArborX::Point;
  using ArborX::Details::verifyDBSCAN;

  ExecutionSpace space;

  {
    auto points = buildView<DeviceType, Point>({{{0, 0, 0}}, {{1, 1, 1}}});

    auto r = std::sqrt(3);

    BOOST_TEST(verifyDBSCAN(space, points, r - 0.1, 2,
                            buildView<DeviceType, int>({-1, -1})));
    BOOST_TEST(!verifyDBSCAN(space, points, r - 0.1, 2,
                             buildView<DeviceType, int>({1, 2})));
    BOOST_TEST(!verifyDBSCAN(space, points, r - 0.1, 2,
                             buildView<DeviceType, int>({1, 1})));
    BOOST_TEST(
        verifyDBSCAN(space, points, r, 2, buildView<DeviceType, int>({1, 1})));
    BOOST_TEST(
        !verifyDBSCAN(space, points, r, 2, buildView<DeviceType, int>({1, 2})));
    BOOST_TEST(verifyDBSCAN(space, points, r, 3,
                            buildView<DeviceType, int>({-1, -1})));
    BOOST_TEST(
        !verifyDBSCAN(space, points, r, 3, buildView<DeviceType, int>({1, 1})));
  }

  {
    auto points = buildView<DeviceType, Point>(
        {{{0, 0, 0}}, {{1, 1, 1}}, {{3, 3, 3}}, {{6, 6, 6}}});

    auto r = std::sqrt(3);

    BOOST_TEST(verifyDBSCAN(space, points, r, 2,
                            buildView<DeviceType, int>({1, 1, -1, -1})));
    BOOST_TEST(verifyDBSCAN(space, points, r, 3,
                            buildView<DeviceType, int>({-1, -1, -1, -1})));

    BOOST_TEST(verifyDBSCAN(space, points, 2 * r, 2,
                            buildView<DeviceType, int>({3, 3, 3, -1})));
    BOOST_TEST(verifyDBSCAN(space, points, 2 * r, 3,
                            buildView<DeviceType, int>({3, 3, 3, -1})));
    BOOST_TEST(verifyDBSCAN(space, points, 2 * r, 4,
                            buildView<DeviceType, int>({-1, -1, -1, -1})));

    BOOST_TEST(verifyDBSCAN(space, points, 3 * r, 2,
                            buildView<DeviceType, int>({5, 5, 5, 5})));
    BOOST_TEST(verifyDBSCAN(space, points, 3 * r, 3,
                            buildView<DeviceType, int>({5, 5, 5, 5})));
    BOOST_TEST(verifyDBSCAN(space, points, 3 * r, 4,
                            buildView<DeviceType, int>({7, 7, 7, 7})));
    BOOST_TEST(verifyDBSCAN(space, points, 3 * r, 5,
                            buildView<DeviceType, int>({-1, -1, -1, -1})));
  }

  {
    // check for bridging effect
    auto points = buildView<DeviceType, Point>({{-1, 0.5, 0},
                                                {-1, -0.5, 0},
                                                {-1, 0, 0},
                                                {{0, 0, 0}},
                                                {{1, 0, 0}},
                                                {{1, 0.5, 0}},
                                                {{1, -0.5, 0}}});

    BOOST_TEST(verifyDBSCAN(space, points, 1, 3,
                            buildView<DeviceType, int>({5, 5, 5, 5, 5, 5, 5})));
    BOOST_TEST(
        (verifyDBSCAN(space, points, 1, 4,
                      buildView<DeviceType, int>({5, 5, 5, 5, 6, 6, 6})) ||
         verifyDBSCAN(space, points, 1, 4,
                      buildView<DeviceType, int>({5, 5, 5, 6, 6, 6, 6}))));
    BOOST_TEST(
        !verifyDBSCAN(space, points, 1, 4,
                      buildView<DeviceType, int>({5, 5, 5, 5, 5, 5, 5})));
  }

  {
    // check where a core point is connected to only boundary points, but which
    // are stripped by a second core point

    // o - core, x - border
    //     -1 0 1 2
    //    ----------
    //  2 | x o x
    //  1 |   x   x
    //  0 |   o x o
    // -1 |   x   x
    // -2 | x o x
    // clang-format off
    auto points = buildView<DeviceType, Point>({
        {0, -2, 0}, {-1, -2, 0}, {1, -2, 0}, {0, -1, 0}, // bottom
        {0, 2, 0}, {-1, 2, 0}, {1, 2, 0}, {0, 1, 0}, // top
        {2, 0, 0}, {2, -1, 0}, {2, 1, 0}, {1, 0, 0}, // right
        {0, 0, 0}  // stripped core
    });
    // clang-format on

    BOOST_TEST(verifyDBSCAN(
        space, points, 1, 4,
        buildView<DeviceType, int>({0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 5})));
    // make sure the stripped core is not marked as noise
    BOOST_TEST(!verifyDBSCAN(
        space, points, 1, 4,
        buildView<DeviceType, int>({0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, -1})));
  }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(dbscan, DeviceType, ARBORX_DEVICE_TYPES)
{
  using ExecutionSpace = typename DeviceType::execution_space;
  using ArborX::dbscan;
  using ArborX::Point;
  using ArborX::Details::verifyDBSCAN;

  ExecutionSpace space;

  for (auto impl : {ArborX::DBSCAN::Implementation::FDBSCAN,
                    ArborX::DBSCAN::Implementation::FDBSCAN_DenseBox})
  {
    ArborX::DBSCAN::Parameters params;
    params.setImplementation(impl);
    {
      auto points = buildView<DeviceType, Point>({{{0, 0, 0}}, {{1, 1, 1}}});

      auto r = std::sqrt(3);

      BOOST_TEST(verifyDBSCAN(space, points, r - 0.1, 2,
                              dbscan(space, points, r - 0.1, 2, params)));
      BOOST_TEST(verifyDBSCAN(space, points, r, 2,
                              dbscan(space, points, r, 2, params)));
      BOOST_TEST(verifyDBSCAN(space, points, r, 3,
                              dbscan(space, points, r, 3, params)));

      // Test non-View primitives
      HiddenView<decltype(points)> hidden_points{points};
      BOOST_TEST(
          verifyDBSCAN(space, hidden_points, r - 0.1, 2,
                       dbscan(space, hidden_points, r - 0.1, 2, params)));
      BOOST_TEST(verifyDBSCAN(space, hidden_points, r, 2,
                              dbscan(space, hidden_points, r, 2, params)));
      BOOST_TEST(verifyDBSCAN(space, hidden_points, r, 3,
                              dbscan(space, hidden_points, r, 3, params)));
    }

    {
      auto points = buildView<DeviceType, Point>(
          {{{0, 0, 0}}, {{1, 1, 1}}, {{3, 3, 3}}, {{6, 6, 6}}});

      auto r = std::sqrt(3);

      BOOST_TEST(verifyDBSCAN(space, points, r, 2,
                              dbscan(space, points, r, 2, params)));
      BOOST_TEST(verifyDBSCAN(space, points, r, 3,
                              dbscan(space, points, r, 3, params)));

      BOOST_TEST(verifyDBSCAN(space, points, 2 * r, 2,
                              dbscan(space, points, 2 * r, 2, params)));
      BOOST_TEST(verifyDBSCAN(space, points, 2 * r, 3,
                              dbscan(space, points, 2 * r, 3, params)));
      BOOST_TEST(verifyDBSCAN(space, points, 2 * r, 4,
                              dbscan(space, points, 2 * r, 4, params)));

      BOOST_TEST(verifyDBSCAN(space, points, 3 * r, 2,
                              dbscan(space, points, 3 * r, 2, params)));
      BOOST_TEST(verifyDBSCAN(space, points, 3 * r, 3,
                              dbscan(space, points, 3 * r, 3, params)));
      BOOST_TEST(verifyDBSCAN(space, points, 3 * r, 4,
                              dbscan(space, points, 3 * r, 4, params)));
      BOOST_TEST(verifyDBSCAN(space, points, 3 * r, 5,
                              dbscan(space, points, 3 * r, 5, params)));
    }

    {
      // check for bridging effect
      auto points = buildView<DeviceType, Point>({{-1, 0.5, 0},
                                                  {-1, -0.5, 0},
                                                  {-1, 0, 0},
                                                  {{0, 0, 0}},
                                                  {{1, 0, 0}},
                                                  {{1, 0.5, 0}},
                                                  {{1, -0.5, 0}}});

      BOOST_TEST(verifyDBSCAN(space, points, 1.0, 3,
                              dbscan(space, points, 1, 3, params)));
      BOOST_TEST(verifyDBSCAN(space, points, 1.0, 4,
                              dbscan(space, points, 1, 4, params)));
    }
  }

  {
    // check where a core point is connected to only boundary points, but which
    // are stripped by a second core point

    // o - core, x - border
    //     -1 0 1 2
    //    ----------
    //  2 | x o x
    //  1 |   x   x
    //  0 |   o x o
    // -1 |   x   x
    // -2 | x o x
    // clang-format off
    auto points = buildView<DeviceType, Point>({
        {0, -2, 0}, {-1, -2, 0}, {1, -2, 0}, {0, -1, 0}, // bottom
        {0, 2, 0}, {-1, 2, 0}, {1, 2, 0}, {0, 1, 0}, // top
        {2, 0, 0}, {2, -1, 0}, {2, 1, 0}, {1, 0, 0}, // right
        {0, 0, 0}  // stripped core
    });
    // clang-format on

    // This does *not* guarantee to trigger the issue, as it depends on the
    // specific implementation and runtime. But it may.
    BOOST_TEST(verifyDBSCAN(space, points, 1, 4, dbscan(space, points, 1, 4)));
  }
}

BOOST_AUTO_TEST_SUITE_END()


/**
 * @file omnicore_utils.cpp
 *
 * This file serves to seperate utility functions from the main mastercore.cpp
 * and mastercore.h files.
 */

#include "omnicore/utils.h"

#include "utilstrencodings.h"

// TODO: use crypto/sha256 instead of openssl
#include "openssl/sha.h"

#include <boost/algorithm/string.hpp>

#include <assert.h>
#include <string.h>
#include <string>
#include <vector>

/**
 * Generates hashes used for obfuscation via ToUpper(HexStr(SHA256(x))).
 *
 * It is expected that the seed has a length of less than 128 characters.
 *
 * @see The class B transaction encoding specification:
 * https://github.com/mastercoin-MSC/spec#class-b-transactions-also-known-as-the-multisig-method
 *
 * @param strSeed[in]      A seed used for the obfuscation
 * @param vstrHashes[out]  The generated hashes
 */
void PrepareObfuscatedHashes(const std::string& strSeed, std::string(&vstrHashes)[1+MAX_SHA256_OBFUSCATION_TIMES])
{
    unsigned char sha_input[128];
    unsigned char sha_result[128];
    std::vector<unsigned char> vec_chars;

    assert(strSeed.size() < sizeof(sha_input));
    strcpy((char *)sha_input, strSeed.c_str());

    // Do only as many re-hashes as there are data packets, 255 per specification
    for (unsigned int j = 1; j <= MAX_SHA256_OBFUSCATION_TIMES; ++j)
    {
        SHA256(sha_input, strlen((const char *)sha_input), sha_result);
        vec_chars.resize(32);
        memcpy(&vec_chars[0], &sha_result[0], 32);
        vstrHashes[j] = HexStr(vec_chars);
        boost::to_upper(vstrHashes[j]); // Convert to upper case characters

        assert(vstrHashes[j].size() < sizeof(sha_input));
        strcpy((char *)sha_input, vstrHashes[j].c_str());
    }
}


// Copyright 2020 the Autoware Foundation
//
// 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.
//
// Co-developed by Tier IV, Inc. and Apex.AI, Inc.

#ifndef TEST_NDT_UTILS_HPP_
#define TEST_NDT_UTILS_HPP_
#include <gtest/gtest.h>
#include <tf2/LinearMath/Quaternion.h>
#include <tf2/LinearMath/Matrix3x3.h>
#include <tf2/LinearMath/Transform.h>
#include <Eigen/Core>
#include "test_ndt_map.hpp"
#include "test_ndt_optimization.hpp"

template<typename PoseT, typename TransformT>
void compare_pose_transform(PoseT & pose, TransformT & transform);

template<typename T>
using EigenPose = Eigen::Matrix<T, 6U, 1U>;
template<typename T>
using EigenTransform = Eigen::Transform<T, 3, Eigen::Affine, Eigen::ColMajor>;
using RosTransform = geometry_msgs::msg::Transform;
using RosPose = geometry_msgs::msg::Pose;

using autoware::localization::ndt::Real;

struct PoseParams
{
  PoseParams(
    double x, double y, double z, double ang_x, double ang_y, double ang_z);

  PoseParams(double translation_range, double rotation_range);
  EigenPose<Real> pose;
};

struct Cov3x3Param
{
  using CovMatrix = Eigen::Matrix<Real, 3, 3>;
  Cov3x3Param(Real e1, Real e2, Real e3, bool valid);
  Real e1, e2, e3;
  CovMatrix cov;
  bool valid{false};
};

template<typename VectorT>
inline auto _x(VectorT & v)->decltype(v.x) &
{
  return v.x;
}

template<typename VectorT>
inline auto _x(VectorT & v)->decltype(v.x()) &
{
  return v.x();
}

template<typename VectorT>
inline auto _y(VectorT & v)->decltype(v.y) &
{
  return v.y;
}

template<typename VectorT>
inline auto _y(VectorT & v)->decltype(v.y()) &
{
  return v.y();
}

template<typename VectorT>
inline auto _z(VectorT & v)->decltype(v.z) &
{
  return v.z;
}

template<typename VectorT>
inline auto _z(VectorT & v)->decltype(v.z()) &
{
  return v.z();
}

template<typename VectorT>
inline auto _w(VectorT & v)->decltype(v.w) &
{
  return v.w;
}

template<typename VectorT>
inline auto _w(VectorT & v)->decltype(v.w()) &
{
  return v.w();
}

template<typename VectorT>
inline auto _x(const VectorT & v)->decltype(v.x) {
  return v.x;
}

template<typename VectorT>
inline auto _x(const VectorT & v)->decltype(v.x()) {
  return v.x();
}

template<typename VectorT>
inline auto _y(const VectorT & v)->decltype(v.y) {
  return v.y;
}

template<typename VectorT>
inline auto _y(const VectorT & v)->decltype(v.y()) {
  return v.y();
}

template<typename VectorT>
inline auto _z(const VectorT & v)->decltype(v.z) {
  return v.z;
}

template<typename VectorT>
inline auto _z(const VectorT & v)->decltype(v.z()) {
  return v.z();
}

template<typename VectorT>
inline auto _w(const VectorT & v)->decltype(v.w) {
  return v.w;
}

template<typename VectorT>
inline auto _w(const VectorT & v)->decltype(v.w()) {
  return v.w();
}

template<typename T1, typename T2>
void set_vector3d(const T1 & t1, T2 & t2)
{
  _x(t2) = _x(t1);
  _y(t2) = _y(t1);
  _z(t2) = _z(t1);
}

template<typename T1, typename T2>
void set_vector4d(const T1 & t1, T2 & t2)
{
  set_vector3d(t1, t2);
  _w(t2) = _w(t1);
}

// tf2 quaternion doesn't expose any references, so an explicit specialization is needed
template<typename T1>
void set_vector4d(const T1 & t1, tf2::Quaternion & t2)
{
  t2.setX(_x(t1));
  t2.setY(_y(t1));
  t2.setZ(_z(t1));
  t2.setW(_w(t1));
}

/// Euler angle -> quaternion conversion on (X,Y,Z) order.
/// Taken from (Henderson, 1977):
/// https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf
/// Page A-2
template<typename QuatT>
void set_RPY_XYZ(
  QuatT & quat,
  const tf2Scalar roll, const tf2Scalar pitch, const tf2Scalar yaw)
{
  tf2Scalar halfYaw = tf2Scalar(yaw) * tf2Scalar(0.5);
  tf2Scalar halfPitch = tf2Scalar(pitch) * tf2Scalar(0.5);
  tf2Scalar halfRoll = tf2Scalar(roll) * tf2Scalar(0.5);
  tf2Scalar cosYaw = tf2Cos(halfYaw);
  tf2Scalar sinYaw = tf2Sin(halfYaw);
  tf2Scalar cosPitch = tf2Cos(halfPitch);
  tf2Scalar sinPitch = tf2Sin(halfPitch);
  tf2Scalar cosRoll = tf2Cos(halfRoll);
  tf2Scalar sinRoll = tf2Sin(halfRoll);
  tf2::Quaternion quat_temp;
  quat_temp.setValue(
    sinRoll * cosPitch * cosYaw + cosRoll * sinPitch * sinYaw,                    // x
    -sinRoll * cosPitch * sinYaw + cosRoll * sinPitch * cosYaw,              // y
    sinRoll * sinPitch * cosYaw + cosRoll * cosPitch * sinYaw,             // z
    -sinRoll * sinPitch * sinYaw + cosRoll * cosPitch * cosYaw);              // w

  // Just making sure this function works for more types.
  set_vector4d(quat_temp, quat);
}

template<typename QuatT, typename AngleT>
void check_quat_RPY_eq(const QuatT & q, const AngleT & RPY)
{
  tf2::Quaternion tf2_quat{_x(q), _y(q), _z(q), _w(q)};
  tf2::Quaternion tf2_quat_from_RPY{};
  // I chose to implement the conversion explicitly based on an actual paper because
  // tf2::Quaternion::setRPY(...) seemed to follow (z,y,x) intrinsic order as seen on:
  // https://en.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions
  set_RPY_XYZ(tf2_quat_from_RPY, _x(RPY), _y(RPY), _z(RPY));

  const auto product = tf2_quat.dot(tf2_quat_from_RPY);   // Either 1 or -1

  EXPECT_FLOAT_EQ(std::fabs(product), 1.0);
  EXPECT_FLOAT_EQ(_x(tf2_quat), product * _x(tf2_quat_from_RPY));
  EXPECT_FLOAT_EQ(_y(tf2_quat), product * _y(tf2_quat_from_RPY));
  EXPECT_FLOAT_EQ(_z(tf2_quat), product * _z(tf2_quat_from_RPY));
  EXPECT_FLOAT_EQ(_w(tf2_quat), product * _w(tf2_quat_from_RPY));
}

template<typename T1, typename T2>
void compare_vector3d_eq(const T1 & t1, const T2 & t2)
{
  EXPECT_FLOAT_EQ(_x(t1), _x(t2));
  EXPECT_FLOAT_EQ(_y(t1), _y(t2));
  EXPECT_FLOAT_EQ(_z(t1), _z(t2));
}

template<typename T1, typename T2>
void compare_vector4d_eq(const T1 & q1, const T2 & q2)
{
  compare_vector3d_eq(q1, q2);
  EXPECT_FLOAT_EQ(_w(q1), _w(q2));
}

template<typename T>
void compare(const EigenPose<T> & pose, const EigenTransform<T> & transform)
{
  Eigen::Matrix<T, 3U, 1U> translation = pose.head(3);
  Eigen::Matrix<T, 3U, 1U> rotation = pose.tail(3);
  compare_vector3d_eq(translation, transform.translation());
  check_quat_RPY_eq(Eigen::Quaternion<T>{transform.rotation()}, rotation);
}

template<typename T>
void compare(const EigenPose<T> & pose, const RosTransform & transform)
{
  Eigen::Matrix<T, 3U, 1U> translation = pose.head(3);
  Eigen::Matrix<T, 3U, 1U> rotation = pose.tail(3);
  compare_vector3d_eq(translation, transform.translation);
  check_quat_RPY_eq(transform.rotation, rotation);
}

template<typename T>
void compare(const EigenPose<T> & pose, const RosPose & ros_pose)
{
  Eigen::Matrix<T, 3U, 1U> translation = pose.head(3);
  Eigen::Matrix<T, 3U, 1U> rotation = pose.tail(3);
  compare_vector3d_eq(translation, ros_pose.position);
  check_quat_RPY_eq(ros_pose.orientation, rotation);
}

template<typename T>
void compare(const EigenPose<T> & pose1, const EigenPose<T> & pose2)
{
  Eigen::Matrix<T, 3U, 1U> translation1 = pose1.head(3);
  Eigen::Matrix<T, 3U, 1U> translation2 = pose2.head(3);
  compare_vector3d_eq(translation1, translation2);
  // Since comparing two set of euler angles is more ambiguous, I am turning one of them into a
  // quaternion and then using `check_quat_RPY_eq`
  Eigen::Matrix<T, 3U, 1U> rotation1 = pose1.tail(3);
  Eigen::Matrix<T, 3U, 1U> rotation2 = pose2.tail(3);
  tf2::Quaternion quat1;
  set_RPY_XYZ(quat1, _x(rotation1), _y(rotation1), _z(rotation1));
  check_quat_RPY_eq(quat1, rotation2);
}

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

template<typename T>
void make_transform(const EigenPose<T> & pose, RosTransform & transform)
{
  set_vector3d(pose.head(3), transform.translation);
  tf2::Quaternion tf2_rot;
  set_RPY_XYZ(tf2_rot, pose(3), pose(4), pose(5));
  set_vector4d(tf2_rot, transform.rotation);
}

template<typename T>
void make_transform(const EigenPose<T> & pose, RosPose & ros_pose)
{
  set_vector3d(pose.head(3), ros_pose.position);
  tf2::Quaternion tf2_rot;
  set_RPY_XYZ(tf2_rot, pose(3), pose(4), pose(5));
  set_vector4d(tf2_rot, ros_pose.orientation);
}

#endif  // TEST_NDT_UTILS_HPP_


// Copyright Matt Overby 2021.
// Distributed under the MIT License.

#ifndef MCL_READELENODE_HPP
#define MCL_READELENODE_HPP 1

#include <Eigen/Core>
#include <fstream>
#include <vector>

namespace mcl
{

static inline bool read_ele_node(
	const std::string& filename_without_extensions, // will look for .node and .ele
	Eigen::MatrixXd &V, // vertices
	Eigen::MatrixXi &T) // tets
{
	using namespace Eigen;

	std::vector<Vector3d> vertices;
	std::vector<Vector4i> tets;
	const std::string& file = filename_without_extensions;

	{ // Load ele

		// Load the vertices of the tetmesh
		std::stringstream ele_file; ele_file << file << ".ele";
		std::ifstream filestream;
		filestream.open( ele_file.str().c_str() );
		if( !filestream ){
			printf("**mcl::read_elenode Error: Could not load %s\n", ele_file.str().c_str());
			return false;
		}

		std::string header;
		std::getline( filestream, header );
		std::stringstream headerSS(header);
		int n_tets = 0; headerSS >> n_tets;

		tets.resize( n_tets );
		std::vector< int > tet_set( n_tets, 0 );
		bool starts_with_one = false;

		for (int i=0; i<n_tets; ++i)
		{
			std::string line;
			std::getline( filestream, line );

			std::stringstream lineSS(line);
			size_t idx;
			int node_ids[4];
			lineSS >> idx >> node_ids[0] >> node_ids[1] >> node_ids[2] >> node_ids[3];

			// Check for 1-indexed
			if( i==0 && idx==1 ){ starts_with_one = true; }
			if( starts_with_one ){
				idx -= 1;
				for( int j=0; j<4; ++j ){ node_ids[j]-=1; }
			}

			if( idx > tets.size() ){
				printf("**mcl::read_elenode Error: Indices are bad for file %s\n", ele_file.str().c_str());
				return false;
			}

			tets[idx] = Vector4i( node_ids[0], node_ids[1], node_ids[2], node_ids[3] );
			tet_set[idx] = 1;
		}
		filestream.close();

		size_t tet_set_size = tet_set.size();
		for( size_t i=0; i<tet_set_size; ++i ){
			if( tet_set[i] == 0 ){
				printf("**mcl::read_elenode Error: Indices are bad for file %s\n", ele_file.str().c_str());
				return false;
			}
		}
	}

	{ // Load node

		// Load the vertices of the tetmesh
		std::stringstream node_file; node_file << file << ".node";
		std::ifstream filestream;
		filestream.open( node_file.str().c_str() );
		if( !filestream ){
			printf("**mcl::read_elenode Error: Could not load %s\n", node_file.str().c_str());
			return false;
		}

		std::string header;
		getline( filestream, header );
		std::stringstream headerSS(header);
		int n_nodes = 0; headerSS >> n_nodes;

		vertices.resize( n_nodes );
		std::vector< int > vertex_set( n_nodes, 0 );
		bool starts_with_one = false;

		for( int i=0; i<n_nodes; ++i ){
			std::string line;
			std::getline( filestream, line );

			std::stringstream lineSS(line);
			double x, y, z;
			size_t idx;
			lineSS >> idx >> x >> y >> z;

			// Check for 1-indexed
			if( i==0 && idx==1 ){ starts_with_one = true; }
			if( starts_with_one ){ idx -= 1; }

			if( idx > vertices.size() ){
				printf("**mcl::read_elenode Error: Indices are bad for file %s\n", node_file.str().c_str());
				return false;
			}

			vertices[idx] = Vector3d( x, y, z );
			vertex_set[idx] = 1;
		}
		filestream.close();

		size_t vert_set = vertex_set.size();
		for( size_t i=0; i<vert_set; ++i ){
			if( vertex_set[i] == 0 ){
				printf("**mcl::read_elenode Error: Indices are bad for file %s\n", node_file.str().c_str());
				return false;
			}
		}

	}

	// Check for inverted tets, and reorder if needed
	int n_tets = tets.size();
	for( int i=0; i<n_tets; ++i ){
		Vector4i tet = tets[i];
		Vector3d a = vertices[tet[0]];
		double vv = (vertices[tet[1]]-a).dot(
			(vertices[tet[2]]-a).cross(vertices[tet[3]]-a)
		) / 6.0;
		if( vv < 0 ){ // reorder base
			tets[i][1] = tet[2];
			tets[i][2] = tet[1];
		}
	}

	if( (int)vertices.size() == 0 || n_tets == 0 ){
		printf("**mcl::read_elenode Error:  Problem loading files\n");
		return false;
	}

	// Copy to V, T
	int nv = vertices.size();
	V.resize( nv, 3 );
	for( int i=0; i<nv; ++i ){ V.row(i) = vertices[i]; }
	T.resize( n_tets, 4 );
	for( int i=0; i<n_tets; ++i ){ T.row(i) = tets[i]; }

	return true;
}

} // namespace mcl

#endif


#include "objparse.h"

#include <cstdio>
#include <iterator>
#include <vector>
#include <iostream>

#include <Eigen/Dense>

#include "parse_common.h"

#pragma GCC visibility push(hidden)

namespace meshparse {

using namespace Eigen;

using std::back_inserter;
using std::cout;
using std::endl;
using std::istream;
using std::istream_iterator;
using std::istringstream;
using std::string;
using std::vector;

struct objface {
    unsigned int id;
    vector<unsigned int> vids;
    vector<unsigned int> nids;
};

vector<struct objface> objfaces;

int obj_parse_vertex_spec(const string vspec) {
    unsigned int vid;
    sscanf(vspec.c_str(), "%d", &vid);
    return vid;
}

void obj_parse_line(mesh &objm, string line) {
    // TODO: support vn directives
    if (line[0] == 'v' && line[1] == ' ') {
        vertex *v = new vertex();

        float x, y, z;
        sscanf(line.c_str(), "v %f %f %f", &x, &y, &z);
        v->loc = Vector3f(x, y, z);
        v->id = objm.verteces.size() + 1;

        objm.verteces.push_back(v);
    } else if (line[0] == 'f' && line[1] == ' ') {
        istringstream tokenizer(line);
        vector<string> tokens;
        copy(istream_iterator<string>(tokenizer),
                istream_iterator<string>(),
                back_inserter<vector<string> >(tokens));

        struct objface f;
        f.id = objfaces.size() + 1;
        for (unsigned int i=1; i<tokens.size(); i++) {
            string token = tokens[i];
            f.vids.push_back(obj_parse_vertex_spec(token));
        }

        objfaces.push_back(f);
    }
}

void obj_add_triangle(mesh &objm, vector<unsigned int> vids) {
    face *f = new face();
    f->id = objm.faces.size() + 1;

    edge *last_edge;
    for (unsigned int i = 0; i < vids.size(); i++) {
        edge *e = new edge();
        e->vert = objm.verteces[vids[i] - 1];
        objm.verteces[vids[i] - 1]->e = e;
        e->f = f;
        e->id = objm.edges.size() + 1;
        e->pair = NULL;
        if (i != 0) {
            last_edge->next = e;
        } else {
            f->e = e;
        }

        last_edge = e;
        objm.edges.push_back(e);
    }
    last_edge->next = f->e;
    f->calculate_normal();
    objm.faces.push_back(f);
}

bool load_obj(istream& file, mesh &mesh) {
    if (!file.good()) {
        cout << "Could not read file." << endl;
        return false;
    }

    string line;
    while (file.good()) {
        getline(file, line);
        obj_parse_line(mesh, line);
    }

    for (auto objf = objfaces.begin(); objf != objfaces.end(); objf++) {
        obj_add_triangle(mesh, objf->vids);
    }
    merge_half_edges(mesh);

    cout << "Loaded mesh: " << endl
        << "  " << mesh.verteces.size() << " verteces." << endl
        << "  " << mesh.edges.size() << " edges." << endl
        << "  " << objfaces.size() << " faces in OBJ file." << endl;
    return true;
}

} // namespace meshparse

#pragma GCC visibility pop


#include "strconvert.hpp"
#include "ast.hpp"
#include "parsers.hpp"
#include "types.hpp"
#include "all.hpp"
#include <boost/variant.hpp>
#include <stdexcept>
#include <sstream>

namespace HT
{
    void strToNum(PASTNode astnode, ParsersHelper& ph)
    {
        validateEach(astnode, ph, "string->number", 1, [](PASTNode an)
                    {
                    return an->token.tokenType == String;
                    });
        astnode->type = Simple;
        astnode->token.tokenType = Complex;
        std::stringstream ss(boost::get<StringType>((*astnode->ch.rbegin())->token.info));
        ComplexType res;
        ss>>res;
        astnode->token.info = res;

        astnode->remove();
    }

}


// The contents of this file are in the public domain. See LICENSE_FOR_EXAMPLE_PROGRAMS.txt
/*

    This is an example illustrating the use of the GUI API as well as some
    aspects of image manipulation from the dlib C++ Library.

    This is a pretty simple example.  It takes a BMP file on the command line
    and opens it up, runs a simple edge detection algorithm on it, and
    displays the results on the screen.
*/

#include <dlib/gui_widgets.h>
#include <dlib/image_io.h>
#include <dlib/image_transforms.h>
#include <fstream>

using namespace std;
using namespace dlib;

//  ----------------------------------------------------------------------------

int main(int argc, char** argv)
{
    try
    {
        // make sure the user entered an argument to this program
        if (argc != 2)
        {
            cout << "error, you have to enter a BMP file as an argument to this program" << endl;
            return 1;
        }

        // Here we declare an image object that can store rgb_pixels.  Note that in
        // dlib there is no explicit image object, just a 2D array and
        // various pixel types.
        array2d<rgb_pixel> img;

        // Now load the image file into our image.  If something is wrong then
        // load_image() will throw an exception.  Also, if you linked with libpng
        // and libjpeg then load_image() can load PNG and JPEG files in addition
        // to BMP files.
        load_image(img, argv[1]);

        // Now let's use some image functions.  First let's blur the image a little.
        array2d<unsigned char> blurred_img;
        gaussian_blur(img, blurred_img);

        // Now find the horizontal and vertical gradient images.
        array2d<short> horz_gradient, vert_gradient;
        array2d<unsigned char> edge_image;
        sobel_edge_detector(blurred_img, horz_gradient, vert_gradient);

        // now we do the non-maximum edge suppression step so that our edges are nice and thin
        suppress_non_maximum_edges(horz_gradient, vert_gradient, edge_image);

        // Now we would like to see what our images look like.  So let's use a
        // window to display them on the screen.  (Note that you can zoom into
        // the window by holding CTRL and scrolling the mouse wheel)
        image_window my_window(edge_image, "Normal Edge Image");

        // We can also easily display the edge_image as a heatmap or using the jet color
        // scheme like so.
        image_window win_hot(heatmap(edge_image));
        image_window win_jet(jet(edge_image));

        // also make a window to display the original image
        image_window my_window2(img, "Original Image");

        // Sometimes you want to get input from the user about which pixels are important
        // for some task.  You can do this easily by trapping user clicks as shown below.
        // This loop executes every time the user double clicks on some image pixel and it
        // will terminate once the user closes the window.
        point p;
        while (my_window.get_next_double_click(p))
        {
            cout << "User double clicked on pixel:         " << p << endl;
            cout << "edge pixel value at this location is: " << (int)edge_image[p.y()][p.x()] << endl;
        }

        // wait until the user closes the windows before we let the program
        // terminate.
        win_hot.wait_until_closed();
        my_window2.wait_until_closed();
    }
    catch (exception& e)
    {
        cout << "exception thrown: " << e.what() << endl;
    }
}

//  ----------------------------------------------------------------------------