// Boost.Geometry

// Copyright (c) 2020-2021, Oracle and/or its affiliates.

// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

// Licensed under the Boost Software License version 1.0.
// http://www.boost.org/users/license.html

#ifndef BOOST_GEOMETRY_STRATEGIES_RELATE_SPHERICAL_HPP
#define BOOST_GEOMETRY_STRATEGIES_RELATE_SPHERICAL_HPP

// TEMP - move to strategy
#include <boost/geometry/strategies/agnostic/point_in_box_by_side.hpp>
#include <boost/geometry/strategies/cartesian/box_in_box.hpp>
#include <boost/geometry/strategies/spherical/intersection.hpp>
#include <boost/geometry/strategies/spherical/point_in_point.hpp>
#include <boost/geometry/strategies/spherical/point_in_poly_winding.hpp>
#include <boost/geometry/strategies/spherical/disjoint_box_box.hpp>

#include <boost/geometry/strategies/envelope/spherical.hpp>
#include <boost/geometry/strategies/relate/services.hpp>
#include <boost/geometry/strategies/detail.hpp>

#include <boost/geometry/strategy/spherical/area.hpp>

#include <boost/geometry/util/type_traits.hpp>

namespace boost { namespace geometry
{

namespace strategies { namespace relate
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail
{

template <typename RadiusTypeOrSphere, typename CalculationType>
class spherical
    : public strategies::envelope::detail::spherical<RadiusTypeOrSphere, CalculationType>
{
    using base_t = strategies::envelope::detail::spherical<RadiusTypeOrSphere, CalculationType>;

public:

    spherical() = default;

    template <typename RadiusOrSphere>
    explicit spherical(RadiusOrSphere const& radius_or_sphere)
        : strategies::envelope::detail::spherical<RadiusTypeOrSphere, CalculationType>(radius_or_sphere)
    {}

    // area

    template <typename Geometry>
    auto area(Geometry const&) const
    {
        return strategy::area::spherical
            <
                typename base_t::radius_type,
                CalculationType
            >(base_t::radius());
    }

    // covered_by

    template <typename Geometry1, typename Geometry2>
    static auto covered_by(Geometry1 const&, Geometry2 const&,
                           std::enable_if_t
                                <
                                    util::is_pointlike<Geometry1>::value
                                 && util::is_box<Geometry2>::value
                                > * = nullptr)
    {
        return strategy::covered_by::spherical_point_box();
    }

    template <typename Geometry1, typename Geometry2>
    static auto covered_by(Geometry1 const&, Geometry2 const&,
                           std::enable_if_t
                            <
                                util::is_box<Geometry1>::value
                             && util::is_box<Geometry2>::value
                            > * = nullptr)
    {
        return strategy::covered_by::spherical_box_box();
    }

    // disjoint

    template <typename Geometry1, typename Geometry2>
    static auto disjoint(Geometry1 const&, Geometry2 const&,
                         std::enable_if_t
                            <
                                util::is_box<Geometry1>::value
                             && util::is_box<Geometry2>::value
                            > * = nullptr)
    {
        return strategy::disjoint::spherical_box_box();
    }

    template <typename Geometry1, typename Geometry2>
    static auto disjoint(Geometry1 const&, Geometry2 const&,
                         std::enable_if_t
                            <
                                util::is_segment<Geometry1>::value
                             && util::is_box<Geometry2>::value
                            > * = nullptr)
    {
        // NOTE: Inconsistent name.
        return strategy::disjoint::segment_box_spherical();
    }

    // relate

    template <typename Geometry1, typename Geometry2>
    static auto relate(Geometry1 const&, Geometry2 const&,
                       std::enable_if_t
                            <
                                util::is_pointlike<Geometry1>::value
                             && util::is_pointlike<Geometry2>::value
                            > * = nullptr)
    {
        return strategy::within::spherical_point_point();
    }

    template <typename Geometry1, typename Geometry2>
    static auto relate(Geometry1 const&, Geometry2 const&,
                       std::enable_if_t
                            <
                                util::is_pointlike<Geometry1>::value
                             && ( util::is_linear<Geometry2>::value
                               || util::is_polygonal<Geometry2>::value )
                            > * = nullptr)
    {
        return strategy::within::spherical_winding<void, void, CalculationType>();
    }

    //template <typename Geometry1, typename Geometry2>
    static auto relate(/*Geometry1 const&, Geometry2 const&,
                       std::enable_if_t
                            <
                                ( util::is_linear<Geometry1>::value
                               || util::is_polygonal<Geometry1>::value )
                             && ( util::is_linear<Geometry2>::value
                               || util::is_polygonal<Geometry2>::value )
                            > * = nullptr*/)
    {
        return strategy::intersection::spherical_segments<CalculationType>();
    }

    // side

    static auto side()
    {
        return strategy::side::spherical_side_formula<CalculationType>();
    }

    // within

    template <typename Geometry1, typename Geometry2>
    static auto within(Geometry1 const&, Geometry2 const&,
                       std::enable_if_t
                            <
                                util::is_pointlike<Geometry1>::value
                                && util::is_box<Geometry2>::value
                            > * = nullptr)
    {
        return strategy::within::spherical_point_box();
    }

    template <typename Geometry1, typename Geometry2>
    static auto within(Geometry1 const&, Geometry2 const&,
                       std::enable_if_t
                            <
                                util::is_box<Geometry1>::value
                             && util::is_box<Geometry2>::value
                            > * = nullptr)
    {
        return strategy::within::spherical_box_box();
    }
};

} // namespace detail
#endif // DOXYGEN_NO_DETAIL

template <typename CalculationType = void>
class spherical
    : public strategies::relate::detail::spherical<void, CalculationType>
{};

namespace services
{

template <typename Geometry1, typename Geometry2>
struct default_strategy<Geometry1, Geometry2, spherical_tag, spherical_tag>
{
    using type = strategies::relate::spherical<>;
};

template <typename Geometry1, typename Geometry2>
struct default_strategy<Geometry1, Geometry2, spherical_equatorial_tag, spherical_equatorial_tag>
{
    using type = strategies::relate::spherical<>;
};

template <typename Geometry1, typename Geometry2>
struct default_strategy<Geometry1, Geometry2, spherical_polar_tag, spherical_polar_tag>
{
    using type = strategies::relate::spherical<>;
};

template <>
struct strategy_converter<strategy::within::spherical_point_point>
{
    static auto get(strategy::within::spherical_point_point const& )
    {
        return strategies::relate::spherical<>();
    }
};

template <>
struct strategy_converter<strategy::within::spherical_point_box>
{
    static auto get(strategy::within::spherical_point_box const&)
    {
        return strategies::relate::spherical<>();
    }
};

template <>
struct strategy_converter<strategy::covered_by::spherical_point_box>
{
    static auto get(strategy::covered_by::spherical_point_box const&)
    {
        return strategies::relate::spherical<>();
    }
};

template <>
struct strategy_converter<strategy::covered_by::spherical_box_box>
{
    static auto get(strategy::covered_by::spherical_box_box const&)
    {
        return strategies::relate::spherical<>();
    }
};

template <>
struct strategy_converter<strategy::disjoint::spherical_box_box>
{
    static auto get(strategy::disjoint::spherical_box_box const&)
    {
        return strategies::relate::spherical<>();
    }
};

template <>
struct strategy_converter<strategy::disjoint::segment_box_spherical>
{
    static auto get(strategy::disjoint::segment_box_spherical const&)
    {
        return strategies::relate::spherical<>();
    }
};

template <>
struct strategy_converter<strategy::within::spherical_box_box>
{
    static auto get(strategy::within::spherical_box_box const&)
    {
        return strategies::relate::spherical<>();
    }
};

template <typename P1, typename P2, typename CalculationType>
struct strategy_converter<strategy::within::spherical_winding<P1, P2, CalculationType>>
{
    static auto get(strategy::within::spherical_winding<P1, P2, CalculationType> const& )
    {
        return strategies::relate::spherical<CalculationType>();
    }
};

template <typename CalculationType>
struct strategy_converter<strategy::intersection::spherical_segments<CalculationType>>
{
    static auto get(strategy::intersection::spherical_segments<CalculationType> const& )
    {
        return strategies::relate::spherical<CalculationType>();
    }
};

template <typename CalculationType>
struct strategy_converter<strategy::within::spherical_point_box_by_side<CalculationType>>
{
    struct altered_strategy
        : strategies::relate::spherical<CalculationType>
    {
        template <typename Geometry1, typename Geometry2>
        static auto covered_by(Geometry1 const&, Geometry2 const&,
                               std::enable_if_t
                                    <
                                        util::is_pointlike<Geometry1>::value
                                     && util::is_box<Geometry2>::value
                                    > * = nullptr)
        {
            return strategy::covered_by::spherical_point_box_by_side<CalculationType>();
        }

        template <typename Geometry1, typename Geometry2>
        static auto within(Geometry1 const&, Geometry2 const&,
                           std::enable_if_t
                                <
                                    util::is_pointlike<Geometry1>::value
                                    && util::is_box<Geometry2>::value
                                > * = nullptr)
        {
            return strategy::within::spherical_point_box_by_side<CalculationType>();
        }
    };

    static auto get(strategy::covered_by::spherical_point_box_by_side<CalculationType> const&)
    {
        return altered_strategy();
    }

    static auto get(strategy::within::spherical_point_box_by_side<CalculationType> const&)
    {
        return altered_strategy();
    }
};

template <typename CalculationType>
struct strategy_converter<strategy::covered_by::spherical_point_box_by_side<CalculationType>>
    : strategy_converter<strategy::within::spherical_point_box_by_side<CalculationType>>
{};

// TEMP used in distance segment/box
template <typename CalculationType>
struct strategy_converter<strategy::side::spherical_side_formula<CalculationType>>
{
    static auto get(strategy::side::spherical_side_formula<CalculationType> const& )
    {
        return strategies::relate::spherical<CalculationType>();
    }
};

} // namespace services

}} // namespace strategies::relate

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_STRATEGIES_RELATE_SPHERICAL_HPP


#ifndef TEST_UNIT_TORSTEN_PK_ONECPT_MODEL_TEST_FIXTURE
#define TEST_UNIT_TORSTEN_PK_ONECPT_MODEL_TEST_FIXTURE

#include <stan/math/rev/mat.hpp>
#include <gtest/gtest.h>
#include <boost/numeric/odeint.hpp>
#include <stan/math/torsten/pmx_onecpt_model.hpp>
#include <stan/math/torsten/pmx_ode_model.hpp>
#include <iostream>
#include <sstream>
#include <vector>
#include <string>

struct TorstenOneCptModelTest : public testing::Test {
  double t0;
  std::vector<double> ts;
  Eigen::Matrix<double, 1, Eigen::Dynamic> y0;
  std::vector<double> rate;
  double CL;
  double V2;
  double ka;
  const std::vector<double> x_r;
  const std::vector<int> x_i;
  std::ostream* msgs;

  void SetUp() {
    // make sure memory's clean before starting each test
    stan::math::recover_memory();
  }
  TorstenOneCptModelTest() :
    t0(0.0),
    ts{0.1, 0.5, 1.0},
    y0(2),
    rate(2, 0.0),
    CL(50.0),
    V2(80.0),
    ka(1.2),
    msgs{nullptr} {
      y0 << 0.0, 0.0;
      SetUp();
  }
};

struct TorstenOneCptTest : public testing::Test {
  int nCmt;
  int nt;
  std::vector<std::vector<double> > pMatrix;
  std::vector<std::vector<double> > biovar;
  std::vector<std::vector<double> > tlag;
  std::vector<double> time;
  std::vector<double> amt;
  std::vector<double> rate;
  std::vector<int> cmt;
  std::vector<int> evid;
  std::vector<double> ii;
  std::vector<int> addl;
  std::vector<int> ss;

  void SetUp() {
    // make sure memory's clean before starting each test
    stan::math::recover_memory();
  }

  TorstenOneCptTest() :
    nCmt(2),
    nt(10),
    pMatrix(1, {10, 80, 1.2}),
    biovar(1, {1, 1}),
    tlag(1, {0, 0}),
    time(nt),
    amt(10, 0),
    rate(10, 0),
    cmt(10, 2),
    evid(10, 0),
    ii(10, 0),
    addl(10, 0),
    ss(10, 0)
  {
    time[0] = 0.0;
    for(int i = 0; i < nt - 1; i++) time[i] = i * 0.25;
    time[nt - 1] = 4.0;

    amt[0] = 1000.0;

    cmt[0] = 1;

    evid[0] = 1;

    ii[0] = 12;

    addl[0] = 14;

    SetUp();
  }

  void resize(int n) {
    nt = n;
    time.resize(nt);
    amt .resize(nt);
    rate.resize(nt);
    cmt .resize(nt);
    evid.resize(nt);
    ii  .resize(nt);
    addl.resize(nt);
    ss  .resize(nt);
  }
};

#endif


#include <iostream>
#include <Eigen/Core>
#include <unsupported/Eigen/AutoDiff>

#include "celerite/solver/cholesky.h"

#define DO_TEST(NAME, VAR1, VAR2)                            \
{                                                            \
  double base, comp, delta;                                  \
  base = VAR1;                                               \
  comp = VAR2;                                               \
  delta = std::abs(base - comp);                             \
  if (delta > 1e-10) {                                       \
    std::cerr << "Test failed: '" << #NAME << "' - error: " << delta << " " << base << " " << comp << std::endl; \
    return 1;                                                \
  } else                                                     \
    std::cerr << "Test passed: '" << #NAME << "' - error: " << delta << std::endl; \
}

int main (int argc, char* argv[])
{
  typedef Eigen::AutoDiffScalar<Eigen::VectorXd> g_t;
  typedef Eigen::Matrix<g_t, Eigen::Dynamic, 1> v_t;
  typedef Eigen::Matrix<g_t, Eigen::Dynamic, Eigen::Dynamic> m_t;

  srand(42);

  size_t N = 1024;
  if (argc >= 2) N = atoi(argv[1]);
  size_t niter = 10;
  if (argc >= 3) niter = atoi(argv[2]);

  // Set up the coefficients.
  size_t p_real = 2, p_complex = 1;
  int nder = 9, j = 1;
  g_t jitter = g_t(0.01, nder, 0);
  v_t a_real(p_real), c_real(p_real),
      a_comp(p_complex), b_comp(p_complex), c_comp(p_complex), d_comp(p_complex);

  a_real << g_t(1.3, nder, j), g_t(1.5, nder, j+1);
  j += 2;
  c_real  << g_t(0.5, nder, j), g_t(0.2, nder, j+1);
  j += 2;
  a_comp << g_t(1.0, nder, j++);
  b_comp << g_t(0.1, nder, j++);
  c_comp << g_t(1.0, nder, j++);
  d_comp << g_t(1.0, nder, j++);

  // Generate some fake data.
  Eigen::VectorXd x = Eigen::VectorXd::Random(N),
                  yerr2 = Eigen::VectorXd::Random(N),
                  y;

  // Set the scale of the uncertainties.
  yerr2.array() *= 0.1;
  yerr2.array() += 0.3;

  // The times need to be sorted.
  std::sort(x.data(), x.data() + x.size());

  // Compute the y values.
  y = sin(x.array());

  Eigen::MatrixXd U(3, N), V(3, N);
  Eigen::VectorXd A(N);
  for (int n = 0; n < N; ++n) {
    A(n) = 1e-6;
    for (int j = 0; j < 3; ++j) {
      U(j, n) = pow(x(n), j);
      V(j, n) = pow(x(n), j) / (1.0 + j);
      A(n) += U(j, n) * V(j, n);
    }
  }

  celerite::solver::CholeskySolver<g_t> cholesky;
  cholesky.compute(jitter, a_real, c_real, a_comp, b_comp, c_comp, d_comp, x, yerr2);
  std::cout << cholesky.log_determinant().derivatives() << std::endl;
  std::cout << cholesky.dot_solve(y).derivatives() << std::endl;

  cholesky.compute(jitter, a_real, c_real, a_comp, b_comp, c_comp, d_comp, A, U, V, x, yerr2);
  std::cout << cholesky.log_determinant().derivatives() << std::endl;
  std::cout << cholesky.dot_solve(y).derivatives() << std::endl;

  v_t empty(0);
  cholesky.compute(jitter, empty, empty, empty, empty, empty, empty, A, U, V, x, yerr2);
  std::cout << cholesky.log_determinant().derivatives() << std::endl;
  std::cout << cholesky.dot_solve(y).derivatives() << std::endl;

  return 0;
}


//==================================================================================================
/**
  Copyright 2017 NumScale SAS

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

#ifndef BOOST_SIMD_FUNCTION_SCALAR_GAMMALN_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_SCALAR_GAMMALN_HPP_INCLUDED

#include <boost/simd/function/definition/gammaln.hpp>
#include <boost/simd/arch/common/scalar/function/gammaln.hpp>

#endif


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

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

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

#include <boost/simd/config.hpp>
#include <boost/dispatch/function/make_callable.hpp>
#include <boost/dispatch/hierarchy/functions.hpp>
#include <boost/simd/detail/dispatch.hpp>

namespace boost { namespace simd
{
  namespace tag
  {
    BOOST_DISPATCH_MAKE_TAG(ext, fast_rsqrt_, boost::dispatch::elementwise_<fast_rsqrt_>);
  }

  namespace ext
  {
    BOOST_DISPATCH_FUNCTION_DECLARATION(tag, fast_rsqrt_);
  }

  BOOST_DISPATCH_CALLABLE_DEFINITION(tag::fast_rsqrt_,fast_rsqrt);

} }

#endif


#pragma once
#ifndef _H_HELPING_VIZ_SOURCE
#define _H_HELPING_VIZ_SOURCE

#define GLM_ENABLE_EXPERIMENTAL

#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#define VIZWIDTH 1024
#define VIZHEIGHT 768

//Include Eigen for matrix functionality.
#include <Eigen/Dense>

#include <string>
#include <iostream>
#include <vector>
#include <array>

#include <nanogui/nanogui.h>

#include <iostream>

#if defined(_WIN32)
#include <windows.h>
#endif

//Mathmatical defines
#define _USE_MATH_DEFINES
#include <math.h>

#include <string>
#include "glm/glm.hpp"
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/quaternion.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtc/type_ptr.hpp>

#include "Scene.h"
#include <Geometry.h>
#include <particlesystem.h>

#include "ShaderSourceCode.h"

struct ToneMapping {
	float whitePoint;   ///Value which will map to full white on display (ie max displayable value without clipping)
	float gamma;        ///Gamma value for display (typically around 2.2 for most monitors)
	bool backfaceCull;  ///Use backface culling? (not really a tonemap parameter but this is a convenient place to put it for now)
};

#endif


/* ----------------------------------------------------------------------
 *
 *                    *** Smooth Mach Dynamics ***
 *
 * This file is part of the USER-SMD package for LAMMPS.
 * Copyright (2014) Georg C. Ganzenmueller, georg.ganzenmueller@emi.fhg.de
 * Fraunhofer Ernst-Mach Institute for High-Speed Dynamics, EMI,
 * Eckerstrasse 4, D-79104 Freiburg i.Br, Germany.
 *
 * ----------------------------------------------------------------------- */

/* ----------------------------------------------------------------------
 LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
 http://lammps.sandia.gov, Sandia National Laboratories
 Steve Plimpton, sjplimp@sandia.gov

 Copyright (2003) Sandia Corporation.  Under the terms of Contract
 DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
 certain rights in this software.  This software is distributed under
 the GNU General Public License.

 See the README file in the top-level LAMMPS directory.
 ------------------------------------------------------------------------- */

#include <cstring>
#include <Eigen/Eigen>
#include "compute_smd_ulsph_strain_rate.h"
#include "atom.h"
#include "update.h"
#include "modify.h"
#include "comm.h"
#include "force.h"
#include "memory.h"
#include "error.h"
#include "pair.h"

using namespace Eigen;
using namespace LAMMPS_NS;

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

ComputeSMDULSPHStrainRate::ComputeSMDULSPHStrainRate(LAMMPS *lmp, int narg, char **arg) :
                Compute(lmp, narg, arg) {
        if (narg != 3)
                error->all(FLERR, "Illegal compute smd/ulsph_strain_rate command");

        peratom_flag = 1;
        size_peratom_cols = 6;

        nmax = 0;
        strain_rate_array = NULL;
}

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

ComputeSMDULSPHStrainRate::~ComputeSMDULSPHStrainRate() {
        memory->sfree(strain_rate_array);
}

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

void ComputeSMDULSPHStrainRate::init() {

        int count = 0;
        for (int i = 0; i < modify->ncompute; i++)
                if (strcmp(modify->compute[i]->style, "smd/ulsph_strain_rate") == 0)
                        count++;
        if (count > 1 && comm->me == 0)
                error->warning(FLERR, "More than one compute smd/ulsph_strain_rate");
}

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

void ComputeSMDULSPHStrainRate::compute_peratom() {
        invoked_peratom = update->ntimestep;
        int *mask = atom->mask;

        // grow vector array if necessary

        if (atom->nmax > nmax) {
                memory->destroy(strain_rate_array);
                nmax = atom->nmax;
                memory->create(strain_rate_array, nmax, size_peratom_cols, "stresstensorVector");
                array_atom = strain_rate_array;
        }

        int itmp = 0;
        Matrix3d *L = (Matrix3d *) force->pair->extract("smd/ulsph/velocityGradient_ptr", itmp);
        if (L == NULL) {
                error->all(FLERR,
                                "compute smd/ulsph_strain_rate could not access any velocity gradients. Are the matching pair styles present?");
        }
        int nlocal = atom->nlocal;
        Matrix3d D;

        for (int i = 0; i < nlocal; i++) {
                if (mask[i] & groupbit) {
                        D = 0.5 * (L[i] + L[i].transpose());
                        strain_rate_array[i][0] = D(0, 0); // xx
                        strain_rate_array[i][1] = D(1, 1); // yy
                        strain_rate_array[i][2] = D(2, 2); // zz
                        strain_rate_array[i][3] = D(0, 1); // xy
                        strain_rate_array[i][4] = D(0, 2); // xz
                        strain_rate_array[i][5] = D(1, 2); // yz
                } else {
                        strain_rate_array[i][0] = 0.0;
                        strain_rate_array[i][1] = 0.0;
                        strain_rate_array[i][2] = 0.0;
                        strain_rate_array[i][3] = 0.0;
                        strain_rate_array[i][4] = 0.0;
                        strain_rate_array[i][5] = 0.0;
                }
        }
}

/* ----------------------------------------------------------------------
 memory usage of local atom-based array
 ------------------------------------------------------------------------- */

double ComputeSMDULSPHStrainRate::memory_usage() {
        double bytes = size_peratom_cols * nmax * sizeof(double);
        return bytes;
}


/***********************************************************************
This file is part of the librjmcmc project source files.

Copyright : Institut Geographique National (2008-2012)
Contributors : Mathieu Brédif, Olivier Tournaire, Didier Boldo
email : librjmcmc@ign.fr

This software is a generic C++ library for stochastic optimization.

This software is governed by the CeCILL license under French law and
abiding by the rules of distribution of free software. You can use,
modify and/or redistribute the software under the terms of the CeCILL
license as circulated by CEA, CNRS and INRIA at the following URL
"http://www.cecill.info".

As a counterpart to the access to the source code and rights to copy,
modify and redistribute granted by the license, users are provided only
with a limited warranty and the software's author, the holder of the
economic rights, and the successive licensors have only limited liability.

In this respect, the user's attention is drawn to the risks associated
with loading, using, modifying and/or developing or reproducing the
software by the user in light of its specific status of free software,
that may mean that it is complicated to manipulate, and that also
therefore means that it is reserved for developers and experienced
professionals having in-depth computer knowledge. Users are therefore
encouraged to load and test the software's suitability as regards their
requirements in conditions enabling the security of their systems and/or
data to be ensured and, more generally, to use and operate it in the
same conditions as regards security.

The fact that you are presently reading this means that you have had
knowledge of the CeCILL license and that you accept its terms.

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

#ifndef GRAPH_CONFIGURATION_HPP
#define GRAPH_CONFIGURATION_HPP

#include <boost/graph/adjacency_list.hpp>
#include "configuration.hpp"
#include "rjmcmc/util/variant.hpp" // apply_visitor

namespace marked_point_process {

    template<typename T, typename UnaryEnergy, typename BinaryEnergy, typename Accelerator=trivial_accelerator, typename OutEdgeList = boost::listS, typename VertexList = boost::listS  >
    class graph_configuration
    {
    public:
	typedef graph_configuration<T,UnaryEnergy, BinaryEnergy, Accelerator, OutEdgeList, VertexList> self;
        typedef T	value_type;
    private:
	class edge {
	public:
            edge() : m_energy(0) {}
            inline double energy() const { return m_energy; }
            inline void energy(double e) { m_energy = e;    }

	private:
            double m_energy;
	};

	class node {
	public:
            node(const value_type& obj, double e) : m_value(obj), m_energy(e) { }
            inline const value_type& value() const { return m_value; }
            inline double energy() const { return m_energy; }

	private:
            value_type	m_value;
            double	m_energy;
	};
	typedef boost::adjacency_list<OutEdgeList, VertexList, boost::undirectedS, node, edge> graph_type;
	typedef typename graph_type::out_edge_iterator	out_edge_iterator;
	typedef	typename graph_type::vertex_descriptor	vertex_descriptor;
	typedef std::pair< typename graph_type::edge_descriptor , bool > edge_descriptor_bool;

    public:
	typedef	typename graph_type::vertex_iterator	iterator;
	typedef	typename graph_type::vertex_iterator	const_iterator;
	typedef typename graph_type::edge_iterator	edge_iterator;
	typedef typename graph_type::edge_iterator	const_edge_iterator;
        typedef internal::modification<self>            modification;
    public:

	// configuration constructors/destructors
	graph_configuration(UnaryEnergy unary_energy, BinaryEnergy binary_energy, Accelerator accelerator=Accelerator()) : m_unary(0.), m_binary(0.), m_unary_energy(unary_energy), m_binary_energy(binary_energy), m_accelerator(accelerator)
	{}
	~graph_configuration()
	{}

	// configuration accessors
	inline double unary_energy () const { return m_unary;}
	inline double binary_energy() const { return m_binary;}
	inline double energy       () const {
            return unary_energy()+binary_energy();
	}

	// values
	inline size_t size() const { return num_vertices(m_graph); }
	inline bool empty() const {	return (num_vertices(m_graph)==0); }
	inline iterator begin() { return vertices(m_graph).first; }
	inline iterator end  () { return vertices(m_graph).second; }
	inline const_iterator begin() const { return vertices(m_graph).first; }
        inline const_iterator end  () const { return vertices(m_graph).second; }
	inline const value_type& value( const_iterator v ) const { return m_graph[ *v ].value(); }
	inline double energy( const_iterator v ) const { return m_graph[ *v ].energy(); }

	// interactions
	inline size_t size_of_interactions   () const { return num_edges(m_graph);    }
	inline edge_iterator interactions_begin() { return edges(m_graph).first; }
	inline edge_iterator interactions_end  () { return edges(m_graph).second; }
	inline const_edge_iterator interactions_begin() const { return edges(m_graph).first; }
	inline const_edge_iterator interactions_end  () const { return edges(m_graph).second; }
	inline double energy( edge_iterator e ) const { return m_graph[ *e ].energy(); }

	// evaluators

	template <typename Modification> double delta_energy(const Modification &modif) const
	{
            return delta_birth(modif)+delta_death(modif);
	}

	template <typename Modification> double delta_birth(const Modification &modif) const
	{
            double delta = 0;
            typedef typename Modification::birth_type::const_iterator bci;
            typedef typename Modification::death_type::const_iterator dci;
            bci bbeg = modif.birth().begin();
            bci bend = modif.birth().end();
            dci dbeg = modif.death().begin();
            dci dend = modif.death().end();
            for(bci it=bbeg; it!=bend; ++it) {
                delta += rjmcmc::apply_visitor(m_unary_energy,*it);
                const_iterator   it2, end2;
                boost::tie(it2,end2)=m_accelerator(*this,*it);
                for (; it2 != end2; ++it2)
                    if (std::find(dbeg,dend,it2)==dend)
                        delta += rjmcmc::apply_visitor(m_binary_energy, *it, value(it2) );
                for (bci it2=bbeg; it2 != it; ++it2)
                    delta += rjmcmc::apply_visitor(m_binary_energy, *it, *it2);
            }
            return delta;
	}

	template <typename Modification> double delta_death(const Modification &modif) const
	{
            double delta = 0;
            typedef typename Modification::death_type::const_iterator dci;
            dci dbeg = modif.death().begin();
            dci dend = modif.death().end();
            for(dci it=dbeg; it!=dend; ++it) {
                iterator v = *it;
                delta -= energy(v);
                out_edge_iterator it2, end;
                for(boost::tie(it2,end) = out_edges( *v, m_graph ); it2!=end; ++it2) {
                    vertex_descriptor dtarget = target(*it2, m_graph);
                    bool found = false;
                    for(dci it3=dbeg; it3!=it && !found; ++it3)
                        found = (**it3 == dtarget);
                    if (!found)
                        delta -= m_graph[ *it2 ].energy();
                }
            }
            return delta;
        }

	// manipulators
	void insert(const value_type& obj)
	{
            node n(obj, rjmcmc::apply_visitor(m_unary_energy,obj));
            m_unary += n.energy();
            vertex_descriptor d = add_vertex(n, m_graph);
            iterator   it, end;

            for (boost::tie(it,end)=m_accelerator(*this,obj); it != end; ++it) {
                if ( *it == d ) continue;
                double e = rjmcmc::apply_visitor(m_binary_energy, obj, value(it) );
                if (   e == 0 ) continue;
                edge_descriptor_bool new_edge = add_edge(d, *it, m_graph );
                m_graph[ new_edge.first ].energy( e );
                m_binary += e;
            }
	}

        template<typename F>
        struct DerefAdapter
        {
            graph_type const & g_;
            F f_;
            DerefAdapter(graph_type const & g, F f) : g_(g), f_(f) {}
            template<typename U> typename F::result_type operator()(U u) { return rjmcmc::apply_visitor(f_,g_[u].value()); }
        };

        template<typename F> inline void for_each(F f)       {
            iterator it, end;
            boost::tie(it,end) = vertices(m_graph);
            DerefAdapter<F> da(m_graph,f);
            std::for_each(it,end,da);
        }
        template<typename F> inline void for_each(F f) const {
            iterator it, end;
            boost::tie(it,end) = vertices(m_graph);
            DerefAdapter<F> da(m_graph,f);
            std::for_each(it,end,da);
        }

	void remove( iterator v )
	{
            out_edge_iterator it, end;
            for(boost::tie(it,end) = out_edges( *v, m_graph ); it!=end; ++it)
                m_binary -= m_graph[ *it ].energy();
            m_unary -= m_graph[*v].energy();
            clear_vertex ( *v , m_graph);
            remove_vertex( *v , m_graph);
	}

	inline void clear() { m_graph.clear(); m_unary=m_binary=0; }

	// audit
	double audit_unary_energy() const
	{
            double e = 0.;
            for (const_iterator i=begin(); i != end(); ++i)
                e += rjmcmc::apply_visitor(m_unary_energy, value(i) );
            return e;
	}

	double audit_binary_energy() const
	{
            double e = 0.;
            const_edge_iterator it, end;
            for(boost::tie(it,end) = edges( m_graph ); it!=end; ++it)
                e += rjmcmc::apply_visitor(m_binary_energy,	m_graph[source(*it,m_graph)].value() ,
                                           m_graph[target(*it,m_graph)].value() );
            return e;
	}

	unsigned int audit_structure() const
	{
            unsigned int err = 0;
            for (const_iterator i=begin(); i != end(); ++i)
            {
                const_iterator j = i;
                for (++j; j != end(); ++j)
                {
                    bool computed = (0!= rjmcmc::apply_visitor(m_binary_energy,value(i), value(j)));
                    bool stored = boost::edge(*i, *j, m_graph).second;
                    if (computed != stored)	++err;
                }
            }
            return err;
	}

    private:
        double m_unary;
        double m_binary;
	graph_type m_graph;
	UnaryEnergy	m_unary_energy;
	BinaryEnergy	m_binary_energy;
        Accelerator	m_accelerator;
    };

}; // namespace marked_point_process

#endif // GRAPH_CONFIGURATION_HPP


/***************************************************************************
 * Copyright 1998-2018 by authors (see AUTHORS.txt)                        *
 *                                                                         *
 *   This file is part of LuxCoreRender.                                   *
 *                                                                         *
 * 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.                                          *
 ***************************************************************************/

#include <boost/format.hpp>

#include "slg/lights/mapspherelight.h"

using namespace std;
using namespace luxrays;
using namespace slg;

//------------------------------------------------------------------------------
// MapSphereLight
//------------------------------------------------------------------------------

MapSphereLight::MapSphereLight() : imageMap(NULL), func(NULL) {
}

MapSphereLight::~MapSphereLight() {
	delete func;
}

void MapSphereLight::Preprocess() {
	SphereLight::Preprocess();

	delete func;
	func = new SampleableSphericalFunction(new ImageMapSphericalFunction(imageMap));
}

void MapSphereLight::GetPreprocessedData(float *localPosData, float *absolutePosData,
		float *emittedFactorData, const SampleableSphericalFunction **funcData) const {
	SphereLight::GetPreprocessedData(localPosData, absolutePosData, emittedFactorData);

	if (funcData)
		*funcData = func;
}

float MapSphereLight::GetPower(const Scene &scene) const {
	return imageMap->GetSpectrumMeanY() * SphereLight::GetPower(scene);
}

Spectrum MapSphereLight::Emit(const Scene &scene,
		const float u0, const float u1, const float u2, const float u3, const float passThroughEvent,
		Point *orig, Vector *dir,
		float *emissionPdfW, float *directPdfA, float *cosThetaAtLight) const {
	// I sample as SphereLight::Emit() instead of func->Sample() because I would
	// have to reject half of the samples due to emitting below the sphere surface
	const Spectrum result = SphereLight::Emit(scene, u0, u1, u2, u3, passThroughEvent,
			orig, dir, emissionPdfW, directPdfA, cosThetaAtLight);

	const Vector localFromLight = Normalize(Inverse(lightToWorld) * (*dir));

	return result *	((SphericalFunction *)func)->Evaluate(localFromLight) /
			func->Average();
}

Spectrum MapSphereLight::Illuminate(const Scene &scene, const Point &p,
		const float u0, const float u1, const float passThroughEvent,
        Vector *dir, float *distance, float *directPdfW,
		float *emissionPdfW, float *cosThetaAtLight) const {
	const Spectrum result = SphereLight::Illuminate(scene, p, u0, u1,
			passThroughEvent, dir, distance, directPdfW,
			emissionPdfW, cosThetaAtLight);

	const Vector localFromLight = Normalize(Inverse(lightToWorld) * (-(*dir)));
	const float funcPdf = func->Pdf(localFromLight);
	if (funcPdf == 0.f)
		return Spectrum();

	return result * ((SphericalFunction *)func)->Evaluate(localFromLight) / func->Average();
}

Properties MapSphereLight::ToProperties(const ImageMapCache &imgMapCache, const bool useRealFileName) const {
	const string prefix = "scene.lights." + GetName();
	Properties props = SphereLight::ToProperties(imgMapCache, useRealFileName);

	props.Set(Property(prefix + ".type")("mappoint"));
	const string fileName = useRealFileName ?
		imageMap->GetName() : imgMapCache.GetSequenceFileName(imageMap);
	props.Set(Property(prefix + ".mapfile")(fileName));
	props.Set(imageMap->ToProperties(prefix, false));

	return props;
}


// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#include "camera.h"

#include "transforms.h"
#include "util.h"

#include <Eigen/Geometry>
#include <Eigen/LU>
#include <cmath>
#include <iostream>
#include <limits>

namespace scenepic
{
  void check_valid_rotation(const Transform& rotation, float tolerance = 1e-6f)
  {
    auto ortho_diff = (rotation * rotation.transpose() - Transform::Identity())
                        .array()
                        .abs()
                        .maxCoeff();
    auto det_diff = std::abs(rotation.determinant() - 1.0f);
    if (ortho_diff > tolerance)
    {
      std::cerr << "camera rotation: not orthogonal within tolerance of "
                << tolerance << ": " << ortho_diff << std::endl;
    }

    if (det_diff > tolerance)
    {
      std::cerr << "camera rotation: |det " << rotation.determinant()
                << " - 1| > " << tolerance << std::endl;
    }
  }

  Camera::Camera(
    const Vector& center,
    const Vector& look_at,
    const Vector& up_dir,
    double fov_y_degrees,
    double near_crop_distance,
    double far_crop_distance,
    double aspect_ratio)
  {
    auto rotation = Transforms::look_at_rotation(center, look_at, up_dir);
    auto translation = Transforms::translate(-center);
    m_world_to_camera = rotation * translation;
    m_camera_to_world = m_world_to_camera.inverse();
    m_projection = Transforms::gl_projection(
      fov_y_degrees, aspect_ratio, near_crop_distance, far_crop_distance);
  }

  Camera::Camera(
    const Vector& center,
    const Transform& rotation,
    double fov_y_degrees,
    double near_crop_distance,
    double far_crop_distance,
    double aspect_ratio)
  {
    check_valid_rotation(rotation);
    auto translation = Transforms::translate(-center);
    m_world_to_camera = rotation * translation;
    m_camera_to_world = m_world_to_camera.inverse();
    m_projection = Transforms::gl_projection(
      fov_y_degrees, aspect_ratio, near_crop_distance, far_crop_distance);
  }

  Camera::Camera(
    const Transform& world_to_camera,
    double fov_y_degrees,
    double near_crop_distance,
    double far_crop_distance,
    double aspect_ratio)
  : m_world_to_camera(world_to_camera)
  {
    if (!m_world_to_camera.isZero())
    {
      check_valid_rotation(this->rotation());
      m_camera_to_world = m_world_to_camera.inverse();
    }

    m_projection = Transforms::gl_projection(
      fov_y_degrees, aspect_ratio, near_crop_distance, far_crop_distance);
  }

  Camera::Camera(const Transform& world_to_camera, const Transform& projection)
  : m_world_to_camera(world_to_camera), m_projection(projection)
  {
    if (!m_world_to_camera.isZero())
    {
      check_valid_rotation(this->rotation());
      m_camera_to_world = m_world_to_camera.inverse();
    }
  }

  Vector Camera::center() const
  {
    Vector center = m_camera_to_world.topRightCorner(3, 1).transpose();
    return center;
  }

  Vector Camera::look_at() const
  {
    Vector look_at(0, 0, -1);
    look_at =
      (look_at.homogeneous() * m_camera_to_world.transpose()).hnormalized();
    return look_at;
  }

  Vector Camera::up_dir() const
  {
    Vector up_dir(0, 1, 0);
    up_dir = (up_dir.homogeneous() * this->rotation()).hnormalized();
    return up_dir;
  }

  Transform Camera::rotation() const
  {
    Transform rotation = m_world_to_camera;
    rotation.topRightCorner(3, 1).fill(0);
    return rotation;
  }

  JsonValue Camera::to_json() const
  {
    JsonValue obj;
    obj["CommandType"] = "SetCamera";
    obj["Value"]["WorldToCamera"] = matrix_to_json(m_world_to_camera);
    obj["Value"]["Projection"] = matrix_to_json(m_projection);
    return obj;
  }

  bool Camera::is_none() const
  {
    return m_projection.isZero() && m_world_to_camera.isZero();
  }

  Camera Camera::None()
  {
    Transform world_to_camera = Transform::Zero();
    Transform projection = Transform::Zero();
    return Camera(world_to_camera, projection);
  }

  std::string Camera::to_string() const
  {
    return this->to_json().to_string();
  }

  const Transform& Camera::world_to_camera() const
  {
    return m_world_to_camera;
  }

  const Transform& Camera::camera_to_world() const
  {
    return m_camera_to_world;
  }

  const Transform& Camera::projection() const
  {
    return m_projection;
  }

  float Camera::aspect_ratio() const
  {
    return m_projection(1, 1) / m_projection(0, 0);
  }

  Camera& Camera::aspect_ratio(float aspect_ratio)
  {
    m_projection(0, 0) = m_projection(1, 1) / aspect_ratio;
    return *this;
  }

  std::vector<Camera> Camera::orbit(
    int num_frames,
    float distance,
    int num_times,
    float min_altitude,
    float max_altitude,
    Vector up_dir,
    Vector forward_dir,
    double fov_y_degrees,
    double aspect_ratio,
    double near_crop_distance,
    double far_crop_distance)
  {
    Vector right_dir = up_dir.cross(forward_dir);
    Eigen::VectorXf azimuth(num_frames);
    Eigen::VectorXf altitude(num_frames);
    azimuth.setLinSpaced(0, num_times * 2 * EIGEN_PI);
    int half_frames = num_frames / 2;
    altitude.topRows(half_frames + 1).setLinSpaced(min_altitude, max_altitude);
    altitude.bottomRows(half_frames).setLinSpaced(min_altitude, max_altitude);
    altitude.bottomRows(half_frames).reverseInPlace();

    Transform projection = Transforms::gl_projection(
      fov_y_degrees, aspect_ratio, near_crop_distance, far_crop_distance);

    Transform init_ext = Transforms::look_at_rotation(
      -forward_dir * distance, Vector::Zero(), up_dir);
    init_ext = Transforms::translate(-forward_dir * distance) * init_ext;
    std::vector<Camera> cameras;
    for (int i = 0; i < num_frames; ++i)
    {
      Transform elevate =
        Transforms::rotation_matrix_from_axis_angle(right_dir, altitude[i]);
      Transform rotate =
        Transforms::rotation_matrix_from_axis_angle(up_dir, azimuth[i]);
      Transform camera_to_world = rotate * elevate * init_ext;
      Transform world_to_camera = camera_to_world.inverse();
      cameras.push_back(Camera(world_to_camera, projection));
    }

    return cameras;
  }

} // namespace scenepic


/* -*-c++-*--------------------------------------------------------------------
 * 2017 Bernd Pfrommer bernd.pfrommer@gmail.com
 */
#include "multicam_tools/overlay_point_cloud_nodelet.h"
#include "multicam_tools/tools.h"
#include <std_msgs/UInt32.h>
#include <boost/range/irange.hpp>
#include <tf_conversions/tf_eigen.h>
#include <tf2/utils.h>
#include <tf2_ros/transform_broadcaster.h>
#include <tf2_ros/transform_listener.h>
#include <pcl/point_types.h>
#include <pcl_ros/point_cloud.h>
#include <pcl/conversions.h>
#include <eigen_conversions/eigen_msg.h>
#include <sstream>
#include <memory>
#include <ctime>
#include <chrono>
#include <cmath>

namespace multicam_tools {
  using boost::irange;

  void OverlayPointCloudNodelet::onInit() {
    ros::NodeHandle nh = getPrivateNodeHandle();
    bool useCalibrationParam(true);
    nh.param<bool>("use_calibration_param", useCalibrationParam, true);
    if (useCalibrationParam) {
      CalibDataVec cdv = CalibrationData::parse_cameras(nh);
      if (cdv.empty()) {
        throw (std::runtime_error("cannot find camera calib data in ros parameters"));
      }
      if (cdv.size() > 1) {
        ROS_WARN("using only camera 0 from calib file!");
      }
      calib_ = cdv[0];
    }
    bool useApproxSync(false);
    nh.getParam("use_approximate_sync", useApproxSync);
    ROS_INFO_STREAM((useApproxSync ? "" : "not ") <<  "using approximate sync");
    nh.param<double>("alpha", alpha_, 0.5);
    image_transport::ImageTransport it(nh);
    std::string img_topic = ros::names::resolve("image");
    std::string points_topic = ros::names::resolve("points");
    ROS_INFO_STREAM("subscribing to image: " << img_topic);
    ROS_INFO_STREAM("subscribing to points: " << points_topic);
    imageSub_.reset(new image_transport::SubscriberFilter());
    imageSub_->subscribe(it,img_topic, 1);
    cloudSub_.reset(new message_filters::Subscriber<PointCloud2Msg>(nh, points_topic, 1));
    if (useApproxSync) {
      approxSync_.reset(new ApproxTimeSynchronizer2(
                          ApproxSyncPolicy2(60/*q size*/), *(imageSub_), *(cloudSub_)));
      approxSync_->registerCallback(&OverlayPointCloudNodelet::syncCallback, this);
    } else {
      exactSync_.reset(new ExactSynchronizer2(*(imageSub_), *(cloudSub_), 2));
      exactSync_->registerCallback(&OverlayPointCloudNodelet::syncCallback, this);
    }
    imagePub_ = nh.advertise<ImageMsg>("image_overlayed", 1);
  }

  void OverlayPointCloudNodelet::syncCallback(ImageConstPtr const &img0, PointCloud2ConstPtr const &pc0) {
    pcl::PointCloud<pcl::PointXYZI> cloud;
    pcl::fromROSMsg(*pc0, cloud);
    cv::Mat projected;
    cv::Mat wp(cloud.points.size(),1, CV_64FC3);
    int pc_idx = 0;
    for (const pcl::PointXYZI &p: cloud.points) {
      wp.at<cv::Vec3d>(pc_idx, 0) = cv::Vec3d(p.x, p.y, p.z);
      pc_idx++;
    }
    const CameraIntrinsics &ci = calib_.intrinsics;
    tools::project_points(wp, identity(), ci.intrinsics,
                          ci.distortion_model, ci.distortion_coeffs,
                          &projected);
    if (imagePub_.getNumSubscribers() > 0) {
      cv_bridge::CvImageConstPtr const cv_ptr = cv_bridge::toCvShare(
        img0, sensor_msgs::image_encodings::MONO8);
      const cv::Mat gray = cv_ptr->image;
      if (gray.rows == 0) {
        ROS_ERROR("cannot decode image, not MONO8!");
        return;
      }
      cv::Mat img(gray.rows, gray.cols, CV_8UC3, cv::Scalar::all(0));
      for (int row = 0; row < projected.rows; row++) {
        int ix = std::nearbyint(projected.at<double>(row, 0));
        int iy = std::nearbyint(projected.at<double>(row, 1));
        if (ix >= 0 && ix < img.cols &&
            iy >= 0 && iy < img.rows) {
          img.at<cv::Vec3b>(iy, ix) = cv::Vec3b(255, 0, 255);
        }
      }
      cv::Mat img_out, gray_rgb;
      cv::cvtColor(gray, gray_rgb, CV_GRAY2BGR);
      addWeighted(img, alpha_, gray_rgb, (1-alpha_), 0.0, img_out);
      cv_bridge::CvImage cv_img(img0->header, sensor_msgs::image_encodings::BGR8, img_out);
      imagePub_.publish(cv_img.toImageMsg());
    }
  }

}


#include <polyfem/RhsAssembler.hpp>
#include <polyfem/par_for.hpp>

#include <polyfem/BoundarySampler.hpp>
#include <polysolve/LinearSolver.hpp>

#include <polyfem/Logger.hpp>

#include <Eigen/Sparse>

#ifdef POLYFEM_WITH_TBB
#include <tbb/parallel_for.h>
#include <tbb/enumerable_thread_specific.h>
#endif

#include <iostream>
#include <map>
#include <memory>

namespace polyfem
{
	using namespace polysolve;
	namespace
	{
		class LocalThreadScalarStorage
		{
		public:
			double val;
			ElementAssemblyValues vals;

			LocalThreadScalarStorage()
			{
				val = 0;
			}
		};
	} // namespace

	RhsAssembler::RhsAssembler(const AssemblerUtils &assembler, const Mesh &mesh,
							   const int n_basis, const int size,
							   const std::vector<ElementBases> &bases, const std::vector<ElementBases> &gbases, const AssemblyValsCache &ass_vals_cache,
							   const std::string &formulation, const Problem &problem,
							   const std::string bc_method,
							   const std::string &solver, const std::string &preconditioner, const json &solver_params)
		: assembler_(assembler), mesh_(mesh),
		  n_basis_(n_basis), size_(size),
		  bases_(bases), gbases_(gbases), ass_vals_cache_(ass_vals_cache),
		  formulation_(formulation), problem_(problem),
		  bc_method_(bc_method),
		  solver_(solver), preconditioner_(preconditioner), solver_params_(solver_params)
	{
	}

	void RhsAssembler::assemble(const Density &density, Eigen::MatrixXd &rhs, const double t) const
	{
		rhs = Eigen::MatrixXd::Zero(n_basis_ * size_, 1);
		if (!problem_.is_rhs_zero())
		{
			Eigen::MatrixXd rhs_fun;

			const int n_elements = int(bases_.size());
			ElementAssemblyValues vals;
			for (int e = 0; e < n_elements; ++e)
			{
				vals.compute(e, mesh_.is_volume(), bases_[e], gbases_[e]);

				const Quadrature &quadrature = vals.quadrature;

				problem_.rhs(assembler_, formulation_, vals.val, t, rhs_fun);

				for (int d = 0; d < size_; ++d)
				{
					//rhs_fun.col(d) = rhs_fun.col(d).array() * vals.det.array() * quadrature.weights.array();
					for (int q = 0; q < quadrature.weights.size(); ++q)
					{
						const double rho = density(vals.val(q, 0), vals.val(q, 1), vals.val.cols() == 2 ? 0. : vals.val(q, 2), vals.element_id);
						rhs_fun(q, d) *= vals.det(q) * quadrature.weights(q) * rho;
					}
				}

				const int n_loc_bases_ = int(vals.basis_values.size());
				for (int i = 0; i < n_loc_bases_; ++i)
				{
					const AssemblyValues &v = vals.basis_values[i];

					for (int d = 0; d < size_; ++d)
					{
						const double rhs_value = (rhs_fun.col(d).array() * v.val.array()).sum();
						for (std::size_t ii = 0; ii < v.global.size(); ++ii)
							rhs(v.global[ii].index * size_ + d) += rhs_value * v.global[ii].val;
					}
				}
			}
		}
	}

	void RhsAssembler::initial_solution(Eigen::MatrixXd &sol) const
	{
		time_bc([&](const Mesh &mesh, const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &pts, Eigen::MatrixXd &val)
				{ problem_.initial_solution(mesh, global_ids, pts, val); },
				sol);
	}

	void RhsAssembler::initial_velocity(Eigen::MatrixXd &sol) const
	{
		time_bc([&](const Mesh &mesh, const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &pts, Eigen::MatrixXd &val)
				{ problem_.initial_velocity(mesh, global_ids, pts, val); },
				sol);
	}

	void RhsAssembler::initial_acceleration(Eigen::MatrixXd &sol) const
	{
		time_bc([&](const Mesh &mesh, const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &pts, Eigen::MatrixXd &val)
				{ problem_.initial_acceleration(mesh, global_ids, pts, val); },
				sol);
	}

	void RhsAssembler::time_bc(const std::function<void(const Mesh &, const Eigen::MatrixXi &, const Eigen::MatrixXd &, Eigen::MatrixXd &)> &fun, Eigen::MatrixXd &sol) const
	{
		sol = Eigen::MatrixXd::Zero(n_basis_ * size_, 1);
		Eigen::MatrixXd loc_sol;

		const int n_elements = int(bases_.size());
		ElementAssemblyValues vals;
		Eigen::MatrixXi ids;
		for (int e = 0; e < n_elements; ++e)
		{
			vals.compute(e, mesh_.is_volume(), bases_[e], gbases_[e]);
			ids.resize(vals.val.rows(), 1);
			ids.setConstant(e);

			const Quadrature &quadrature = vals.quadrature;
			//problem_.initial_solution(vals.val, loc_sol);
			fun(mesh_, ids, vals.val, loc_sol);

			for (int d = 0; d < size_; ++d)
				loc_sol.col(d) = loc_sol.col(d).array() * vals.det.array() * quadrature.weights.array();

			const int n_loc_bases_ = int(vals.basis_values.size());
			for (int i = 0; i < n_loc_bases_; ++i)
			{
				const AssemblyValues &v = vals.basis_values[i];

				for (int d = 0; d < size_; ++d)
				{
					const double sol_value = (loc_sol.col(d).array() * v.val.array()).sum();
					for (std::size_t ii = 0; ii < v.global.size(); ++ii)
						sol(v.global[ii].index * size_ + d) += sol_value * v.global[ii].val;
				}
			}
		}

		Eigen::MatrixXd b = sol;
		sol.setZero();

		const double mmin = b.minCoeff();
		const double mmax = b.maxCoeff();

		if (fabs(mmin) > 1e-8 || fabs(mmax) > 1e-8)
		{
			StiffnessMatrix mass;
			Density d;
			assembler_.assemble_mass_matrix(formulation_, size_ == 3, n_basis_, d, bases_, gbases_, ass_vals_cache_, mass);
			auto solver = LinearSolver::create(solver_, preconditioner_);
			solver->setParameters(solver_params_);
			solver->analyzePattern(mass, mass.rows());
			solver->factorize(mass);

			for (long i = 0; i < b.cols(); ++i)
			{
				solver->solve(b.col(i), sol.col(i));
			}
			logger().trace("mass matrix error {}", (mass * sol - b).norm());
		}
	}

	void RhsAssembler::lsq_bc(const std::function<void(const Eigen::MatrixXi &, const Eigen::MatrixXd &, const Eigen::MatrixXd &, Eigen::MatrixXd &)> &df,
							  const std::vector<LocalBoundary> &local_boundary, const std::vector<int> &bounday_nodes, const int resolution, Eigen::MatrixXd &rhs) const
	{
		const int n_el = int(bases_.size());

		Eigen::MatrixXd uv, samples, gtmp, rhs_fun;
		Eigen::VectorXi global_primitive_ids;

		int index = 0;
		std::vector<int> indices;
		indices.reserve(n_el * 10);
		// std::map<int, int> global_index_to_col;

		long total_size = 0;

		Eigen::Matrix<bool, Eigen::Dynamic, 1> is_boundary(n_basis_);
		is_boundary.setConstant(false);
		Eigen::VectorXi global_index_to_col(n_basis_);
		global_index_to_col.setConstant(-1);

		const int actual_dim = problem_.is_scalar() ? 1 : mesh_.dimension();

		// assert((bounday_nodes.size()/actual_dim)*actual_dim == bounday_nodes.size());

		int skipped_count = 0;
		for (int b : bounday_nodes)
		{
			int bindex = b / actual_dim;

			if (bindex < is_boundary.size())
				is_boundary[bindex] = true;
			else
				skipped_count++;
		}
		assert(skipped_count <= 1);

		for (const auto &lb : local_boundary)
		{
			const int e = lb.element_id();
			bool has_samples = sample_boundary(lb, resolution, true, uv, samples, global_primitive_ids);

			if (!has_samples)
				continue;

			const ElementBases &bs = bases_[e];
			const int n_local_bases = int(bs.bases.size());

			total_size += samples.rows();

			for (int j = 0; j < n_local_bases; ++j)
			{
				const Basis &b = bs.bases[j];

				for (std::size_t ii = 0; ii < b.global().size(); ++ii)
				{
					//pt found
					// if(std::find(bounday_nodes.begin(), bounday_nodes.end(), size_ * b.global()[ii].index) != bounday_nodes.end())
					if (is_boundary[b.global()[ii].index])
					{
						// if(!global_index_to_col.contains(b.global()[ii].index))
						if (global_index_to_col(b.global()[ii].index) == -1)
						{
							// global_index_to_col[b.global()[ii].index] = index++;
							global_index_to_col(b.global()[ii].index) = index++;
							indices.push_back(b.global()[ii].index);
							assert(indices.size() == size_t(index));
						}
					}
				}
			}
		}

		// Eigen::MatrixXd global_mat = Eigen::MatrixXd::Zero(total_size, indices.size());
		Eigen::MatrixXd global_rhs = Eigen::MatrixXd::Zero(total_size, size_);

		const long buffer_size = total_size * long(indices.size());
		std::vector<Eigen::Triplet<double>> entries, entries_t;
		// entries.reserve(buffer_size);
		// entries_t.reserve(buffer_size);

		index = 0;

		int global_counter = 0;
		Eigen::MatrixXd mapped;

		std::vector<AssemblyValues> tmp_val;

		for (const auto &lb : local_boundary)
		{
			const int e = lb.element_id();
			bool has_samples = sample_boundary(lb, resolution, false, uv, samples, global_primitive_ids);

			if (!has_samples)
				continue;

			const ElementBases &bs = bases_[e];
			const ElementBases &gbs = gbases_[e];
			const int n_local_bases = int(bs.bases.size());

			gbs.eval_geom_mapping(samples, mapped);

			bs.evaluate_bases(samples, tmp_val);
			for (int j = 0; j < n_local_bases; ++j)
			{
				const Basis &b = bs.bases[j];
				const auto &tmp = tmp_val[j].val;

				for (std::size_t ii = 0; ii < b.global().size(); ++ii)
				{
					// auto item = global_index_to_col.find(b.global()[ii].index);
					// if(item != global_index_to_col.end()){
					auto item = global_index_to_col(b.global()[ii].index);
					if (item != -1)
					{
						for (int k = 0; k < int(tmp.size()); ++k)
						{
							// entries.push_back(Eigen::Triplet<double>(global_counter+k, item->second, tmp(k, j) * b.global()[ii].val));
							// entries_t.push_back(Eigen::Triplet<double>(item->second, global_counter+k, tmp(k, j) * b.global()[ii].val));
							entries.push_back(Eigen::Triplet<double>(global_counter + k, item, tmp(k) * b.global()[ii].val));
							entries_t.push_back(Eigen::Triplet<double>(item, global_counter + k, tmp(k) * b.global()[ii].val));
						}
						// global_mat.block(global_counter, item->second, tmp.size(), 1) = tmp;
					}
				}
			}

			// problem_.bc(mesh_, global_primitive_ids, mapped, t, rhs_fun);
			df(global_primitive_ids, uv, mapped, rhs_fun);
			global_rhs.block(global_counter, 0, rhs_fun.rows(), rhs_fun.cols()) = rhs_fun;
			global_counter += rhs_fun.rows();

			//UIState::ui_state().debug_data().add_points(mapped, Eigen::MatrixXd::Constant(1, 3, 0));

			//Eigen::MatrixXd asd(mapped.rows(), 3);
			//asd.col(0)=mapped.col(0);
			//asd.col(1)=mapped.col(1);
			//asd.col(2)=rhs_fun;
			//UIState::ui_state().debug_data().add_points(asd, Eigen::MatrixXd::Constant(1, 3, 0));
		}

		assert(global_counter == total_size);

		if (total_size > 0)
		{
			const double mmin = global_rhs.minCoeff();
			const double mmax = global_rhs.maxCoeff();

			if (fabs(mmin) < 1e-8 && fabs(mmax) < 1e-8)
			{
				// std::cout<<"is all zero, skipping"<<std::endl;
				for (size_t i = 0; i < indices.size(); ++i)
				{
					for (int d = 0; d < size_; ++d)
					{
						if (problem_.all_dimensions_dirichlet() || std::find(bounday_nodes.begin(), bounday_nodes.end(), indices[i] * size_ + d) != bounday_nodes.end())
							rhs(indices[i] * size_ + d) = 0;
					}
				}
			}
			else
			{
				StiffnessMatrix mat(int(total_size), int(indices.size()));
				mat.setFromTriplets(entries.begin(), entries.end());

				StiffnessMatrix mat_t(int(indices.size()), int(total_size));
				mat_t.setFromTriplets(entries_t.begin(), entries_t.end());

				StiffnessMatrix A = mat_t * mat;
				Eigen::MatrixXd b = mat_t * global_rhs;

				Eigen::MatrixXd coeffs(b.rows(), b.cols());
				auto solver = LinearSolver::create(solver_, preconditioner_);
				solver->setParameters(solver_params_);
				solver->analyzePattern(A, A.rows());
				solver->factorize(A);
				coeffs.setZero();
				for (long i = 0; i < b.cols(); ++i)
				{
					solver->solve(b.col(i), coeffs.col(i));
				}
				logger().trace("RHS solve error {}", (A * coeffs - b).norm());

				for (long i = 0; i < coeffs.rows(); ++i)
				{
					for (int d = 0; d < size_; ++d)
					{
						if (problem_.all_dimensions_dirichlet() || std::find(bounday_nodes.begin(), bounday_nodes.end(), indices[i] * size_ + d) != bounday_nodes.end())
							rhs(indices[i] * size_ + d) = coeffs(i, d);
					}
				}
			}
		}
	}

	void RhsAssembler::sample_bc(const std::function<void(const Eigen::MatrixXi &, const Eigen::MatrixXd &, const Eigen::MatrixXd &, Eigen::MatrixXd &)> &df,
								 const std::vector<LocalBoundary> &local_boundary, const std::vector<int> &bounday_nodes, Eigen::MatrixXd &rhs) const
	{
		const int n_el = int(bases_.size());

		Eigen::MatrixXd rhs_fun;
		Eigen::VectorXi global_primitive_ids(1);
		Eigen::MatrixXd nans(1, 1);
		nans(0) = std::nan("");

#ifndef NDEBUG
		Eigen::Matrix<bool, Eigen::Dynamic, 1> is_boundary(n_basis_);
		is_boundary.setConstant(false);

		const int actual_dim = problem_.is_scalar() ? 1 : mesh_.dimension();

		int skipped_count = 0;
		for (int b : bounday_nodes)
		{
			int bindex = b / actual_dim;

			if (bindex < is_boundary.size())
				is_boundary[bindex] = true;
			else
				skipped_count++;
		}
		assert(skipped_count <= 1);
#endif

		for (const auto &lb : local_boundary)
		{
			const int e = lb.element_id();
			const ElementBases &bs = bases_[e];

			for (int i = 0; i < lb.size(); ++i)
			{
				global_primitive_ids(0) = lb.global_primitive_id(i);
				const auto nodes = bs.local_nodes_for_primitive(global_primitive_ids(0), mesh_);

				for (long n = 0; n < nodes.size(); ++n)
				{
					const auto &b = bs.bases[nodes(n)];
					const auto &glob = b.global();

					for (size_t ii = 0; ii < glob.size(); ++ii)
					{
						assert(is_boundary[glob[ii].index]);

						//TODO, missing UV!!!!
						df(global_primitive_ids, nans, glob[ii].node, rhs_fun);

						for (int d = 0; d < size_; ++d)
						{
							if (problem_.all_dimensions_dirichlet() || std::find(bounday_nodes.begin(), bounday_nodes.end(), glob[ii].index * size_ + d) != bounday_nodes.end())
								rhs(glob[ii].index * size_ + d) = rhs_fun(0, d);
						}
					}
				}
			}
		}
	}

	void RhsAssembler::integrate_bc(const std::function<void(const Eigen::MatrixXi &, const Eigen::MatrixXd &, const Eigen::MatrixXd &, Eigen::MatrixXd &)> &df,
									const std::vector<LocalBoundary> &local_boundary, const std::vector<int> &bounday_nodes, const int resolution, Eigen::MatrixXd &rhs) const
	{
		Eigen::MatrixXd uv, samples, rhs_fun, normals, mapped;
		Eigen::VectorXd weights;

		Eigen::VectorXi global_primitive_ids;
		std::vector<AssemblyValues> tmp_val;

		Eigen::Matrix<bool, Eigen::Dynamic, 1> is_boundary(n_basis_);
		is_boundary.setConstant(false);

		Eigen::MatrixXd areas(rhs.rows(), 1);
		areas.setZero();

		const int actual_dim = problem_.is_scalar() ? 1 : mesh_.dimension();

		int skipped_count = 0;
		for (int b : bounday_nodes)
		{
			rhs(b) = 0;
			int bindex = b / actual_dim;

			if (bindex < is_boundary.size())
				is_boundary[bindex] = true;
			else
				skipped_count++;
		}
		assert(skipped_count <= 1);
		ElementAssemblyValues vals;

		for (const auto &lb : local_boundary)
		{
			const int e = lb.element_id();
			bool has_samples = boundary_quadrature(lb, resolution, false, uv, samples, normals, weights, global_primitive_ids);

			if (!has_samples)
				continue;

			const ElementBases &bs = bases_[e];
			const ElementBases &gbs = gbases_[e];

			vals.compute(e, mesh_.is_volume(), samples, bs, gbs);

			df(global_primitive_ids, uv, vals.val, rhs_fun);

			for (int d = 0; d < size_; ++d)
				rhs_fun.col(d) = rhs_fun.col(d).array() * weights.array();

			for (int i = 0; i < lb.size(); ++i)
			{
				const int primitive_global_id = lb.global_primitive_id(i);
				const auto nodes = bs.local_nodes_for_primitive(primitive_global_id, mesh_);

				for (long n = 0; n < nodes.size(); ++n)
				{
					// const auto &b = bs.bases[nodes(n)];
					const AssemblyValues &v = vals.basis_values[nodes(n)];
					const double area = (weights.array() * v.val.array()).sum();
					for (int d = 0; d < size_; ++d)
					{
						const double rhs_value = (rhs_fun.col(d).array() * v.val.array()).sum();

						for (size_t g = 0; g < v.global.size(); ++g)
						{
							const int g_index = v.global[g].index * size_ + d;
							if (problem_.all_dimensions_dirichlet() || std::find(bounday_nodes.begin(), bounday_nodes.end(), g_index) != bounday_nodes.end())
							{
								rhs(g_index) += rhs_value * v.global[g].val;
								areas(g_index) += area * v.global[g].val;
							}
						}
					}
				}
			}
		}

		for (int b : bounday_nodes)
		{
			assert(areas(b) != 0);
			rhs(b) /= areas(b);
		}
	}

	void RhsAssembler::set_bc(
		const std::function<void(const Eigen::MatrixXi &, const Eigen::MatrixXd &, const Eigen::MatrixXd &, Eigen::MatrixXd &)> &df,
		const std::function<void(const Eigen::MatrixXi &, const Eigen::MatrixXd &, const Eigen::MatrixXd &, const Eigen::MatrixXd &, Eigen::MatrixXd &)> &nf,
		const std::vector<LocalBoundary> &local_boundary, const std::vector<int> &bounday_nodes, const int resolution, const std::vector<LocalBoundary> &local_neumann_boundary, Eigen::MatrixXd &rhs) const
	{
		if (bc_method_ == "sample")
			sample_bc(df, local_boundary, bounday_nodes, rhs);
		else if (bc_method_ == "integrate")
			integrate_bc(df, local_boundary, bounday_nodes, resolution, rhs);
		else
			lsq_bc(df, local_boundary, bounday_nodes, resolution, rhs);

		//Neumann
		Eigen::MatrixXd uv, samples, gtmp, rhs_fun;
		Eigen::VectorXi global_primitive_ids;
		Eigen::MatrixXd points, normals;
		Eigen::VectorXd weights;

		ElementAssemblyValues vals;

		for (const auto &lb : local_neumann_boundary)
		{
			const int e = lb.element_id();
			bool has_samples = boundary_quadrature(lb, resolution, false, uv, points, normals, weights, global_primitive_ids);

			if (!has_samples)
				continue;

			const ElementBases &gbs = gbases_[e];
			const ElementBases &bs = bases_[e];

			vals.compute(e, mesh_.is_volume(), points, bs, gbs);

			for (int n = 0; n < vals.jac_it.size(); ++n)
			{
				normals.row(n) = normals.row(n) * vals.jac_it[n];
				normals.row(n).normalize();
			}
			// problem_.neumann_bc(mesh_, global_primitive_ids, vals.val, t, rhs_fun);
			nf(global_primitive_ids, uv, vals.val, normals, rhs_fun);

			// UIState::ui_state().debug_data().add_points(vals.val, Eigen::RowVector3d(0,1,0));

			for (int d = 0; d < size_; ++d)
				rhs_fun.col(d) = rhs_fun.col(d).array() * weights.array();

			for (int i = 0; i < lb.size(); ++i)
			{
				const int primitive_global_id = lb.global_primitive_id(i);
				const auto nodes = bs.local_nodes_for_primitive(primitive_global_id, mesh_);

				for (long n = 0; n < nodes.size(); ++n)
				{
					// const auto &b = bs.bases[nodes(n)];
					const AssemblyValues &v = vals.basis_values[nodes(n)];
					for (int d = 0; d < size_; ++d)
					{
						const double rhs_value = (rhs_fun.col(d).array() * v.val.array()).sum();

						for (size_t g = 0; g < v.global.size(); ++g)
						{
							const int g_index = v.global[g].index * size_ + d;
							const bool is_neumann = std::find(bounday_nodes.begin(), bounday_nodes.end(), g_index) == bounday_nodes.end();

							if (is_neumann)
							{
								rhs(g_index) += rhs_value * v.global[g].val;
							}
						}
					}
				}
			}
		}
	}

	void RhsAssembler::set_bc(const std::vector<LocalBoundary> &local_boundary, const std::vector<int> &bounday_nodes, const int resolution, const std::vector<LocalBoundary> &local_neumann_boundary, Eigen::MatrixXd &rhs, const double t) const
	{
		set_bc(
			[&](const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &uv, const Eigen::MatrixXd &pts, Eigen::MatrixXd &val)
			{ problem_.bc(mesh_, global_ids, uv, pts, t, val); },
			[&](const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &uv, const Eigen::MatrixXd &pts, const Eigen::MatrixXd &normals, Eigen::MatrixXd &val)
			{ problem_.neumann_bc(mesh_, global_ids, uv, pts, normals, t, val); },
			local_boundary, bounday_nodes, resolution, local_neumann_boundary, rhs);
	}

	void RhsAssembler::set_velocity_bc(const std::vector<LocalBoundary> &local_boundary, const std::vector<int> &bounday_nodes, const int resolution, const std::vector<LocalBoundary> &local_neumann_boundary, Eigen::MatrixXd &rhs, const double t) const
	{
		set_bc(
			[&](const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &uv, const Eigen::MatrixXd &pts, Eigen::MatrixXd &val)
			{ problem_.velocity_bc(mesh_, global_ids, uv, pts, t, val); },
			[&](const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &uv, const Eigen::MatrixXd &pts, const Eigen::MatrixXd &normals, Eigen::MatrixXd &val)
			{ problem_.neumann_velocity_bc(mesh_, global_ids, uv, pts, normals, t, val); },
			local_boundary, bounday_nodes, resolution, local_neumann_boundary, rhs);
	}

	void RhsAssembler::set_acceleration_bc(const std::vector<LocalBoundary> &local_boundary, const std::vector<int> &bounday_nodes, const int resolution, const std::vector<LocalBoundary> &local_neumann_boundary, Eigen::MatrixXd &rhs, const double t) const
	{
		set_bc(
			[&](const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &uv, const Eigen::MatrixXd &pts, Eigen::MatrixXd &val)
			{ problem_.acceleration_bc(mesh_, global_ids, uv, pts, t, val); },
			[&](const Eigen::MatrixXi &global_ids, const Eigen::MatrixXd &uv, const Eigen::MatrixXd &pts, const Eigen::MatrixXd &normals, Eigen::MatrixXd &val)
			{ problem_.neumann_acceleration_bc(mesh_, global_ids, uv, pts, normals, t, val); },
			local_boundary, bounday_nodes, resolution, local_neumann_boundary, rhs);
	}

	void RhsAssembler::compute_energy_grad(const std::vector<LocalBoundary> &local_boundary, const std::vector<int> &bounday_nodes, const Density &density, const int resolution, const std::vector<LocalBoundary> &local_neumann_boundary, const Eigen::MatrixXd &final_rhs, const double t, Eigen::MatrixXd &rhs) const
	{
		if (problem_.is_constant_in_time())
		{
			rhs = final_rhs;
		}
		else
		{
			assemble(density, rhs, t);
			rhs *= -1;
			// set_bc(local_boundary, bounday_nodes, resolution, local_neumann_boundary, rhs, t);

			if (rhs.size() != final_rhs.size())
			{
				const int prev_size = rhs.size();
				rhs.conservativeResize(final_rhs.size(), rhs.cols());
				//Zero initial pressure
				rhs.block(prev_size, 0, final_rhs.size() - prev_size, rhs.cols()).setZero();
				rhs(rhs.size() - 1) = 0;
			}

			assert(rhs.size() == final_rhs.size());
		}
	}

	double RhsAssembler::compute_energy(const Eigen::MatrixXd &displacement, const std::vector<LocalBoundary> &local_neumann_boundary, const Density &density, const int resolution, const double t) const
	{

		double res = 0;

		if (!problem_.is_rhs_zero())
		{
#if defined(POLYFEM_WITH_CPP_THREADS)
			std::vector<LocalThreadScalarStorage> storages(polyfem::get_n_threads());
#elif defined(POLYFEM_WITH_TBB)
			typedef tbb::enumerable_thread_specific<LocalThreadScalarStorage> LocalStorage;
			LocalStorage storages((LocalThreadScalarStorage()));
#else
			LocalThreadScalarStorage loc_storage;
			Eigen::MatrixXd forces;
#endif

			const int n_bases = int(bases_.size());

#if defined(POLYFEM_WITH_CPP_THREADS)
			polyfem::par_for(n_bases, [&](int start, int end, int t)
							 {
								 auto &loc_storage = storages[t];
								 Eigen::Matrix<double, Eigen::Dynamic, 1, 0, 3, 1> local_displacement(size_);
								 Eigen::MatrixXd forces;
								 for (int e = start; e < end; ++e)
								 {
#elif defined(POLYFEM_WITH_TBB)
			tbb::parallel_for(tbb::blocked_range<int>(0, n_bases), [&](const tbb::blocked_range<int> &r) {
				LocalStorage::reference loc_storage = storages.local();
				Eigen::Matrix<double, Eigen::Dynamic, 1, 0, 3, 1> local_displacement(size_);
				Eigen::MatrixXd forces;
				for (int e = r.begin(); e != r.end(); ++e)
				{
#else
			Eigen::Matrix<double, Eigen::Dynamic, 1, 0, 3, 1> local_displacement(size_);

			for (int e = 0; e < n_bases; ++e)
			{
#endif
									 ElementAssemblyValues &vals = loc_storage.vals;
									 vals.compute(e, mesh_.is_volume(), bases_[e], gbases_[e]);

									 const Quadrature &quadrature = vals.quadrature;
									 const Eigen::VectorXd da = vals.det.array() * quadrature.weights.array();

									 problem_.rhs(assembler_, formulation_, vals.val, t, forces);
									 assert(forces.rows() == da.size());
									 assert(forces.cols() == size_);

									 for (long p = 0; p < da.size(); ++p)
									 {
										 local_displacement.setZero();

										 for (size_t i = 0; i < vals.basis_values.size(); ++i)
										 {
											 const auto &bs = vals.basis_values[i];
											 assert(bs.val.size() == da.size());
											 const double b_val = bs.val(p);

											 for (int d = 0; d < size_; ++d)
											 {
												 for (std::size_t ii = 0; ii < bs.global.size(); ++ii)
												 {
													 local_displacement(d) += (bs.global[ii].val * b_val) * displacement(bs.global[ii].index * size_ + d);
												 }
											 }
										 }
										 const double rho = density(vals.val(p, 0), vals.val(p, 1), vals.val.cols() == 2 ? 0. : vals.val(p, 2), vals.element_id);

										 for (int d = 0; d < size_; ++d)
										 {
											 loc_storage.val += forces(p, d) * local_displacement(d) * da(p) * rho;
											 // res += forces(p, d) * local_displacement(d) * da(p);
										 }
									 }
#if defined(POLYFEM_WITH_CPP_THREADS) || defined(POLYFEM_WITH_TBB)
								 }
							 });
#else
				}
#endif

#if defined(POLYFEM_WITH_CPP_THREADS)
			for (const auto &t : storages)
			{
				res += t.val;
			}
#elif defined(POLYFEM_WITH_TBB)
				for (LocalStorage::iterator i = storages.begin(); i != storages.end(); ++i)
				{
					res += i->val;
				}
#else
				res = loc_storage.val;
#endif
		}

		Eigen::Matrix<double, Eigen::Dynamic, 1, 0, 3, 1> local_displacement(size_);
		Eigen::MatrixXd forces;

		ElementAssemblyValues vals;
		//Neumann
		Eigen::MatrixXd points, uv, normals;
		Eigen::VectorXd weights;
		Eigen::VectorXi global_primitive_ids;
		for (const auto &lb : local_neumann_boundary)
		{
			const int e = lb.element_id();
			bool has_samples = boundary_quadrature(lb, resolution, false, uv, points, normals, weights, global_primitive_ids);

			if (!has_samples)
				continue;

			const ElementBases &gbs = gbases_[e];
			const ElementBases &bs = bases_[e];

			vals.compute(e, mesh_.is_volume(), points, bs, gbs);

			for (int n = 0; n < vals.jac_it.size(); ++n)
			{
				normals.row(n) = normals.row(n) * vals.jac_it[n];
				normals.row(n).normalize();
			}
			problem_.neumann_bc(mesh_, global_primitive_ids, uv, vals.val, normals, t, forces);

			// UIState::ui_state().debug_data().add_points(vals.val, Eigen::RowVector3d(1,0,0));

			for (long p = 0; p < weights.size(); ++p)
			{
				local_displacement.setZero();

				for (size_t i = 0; i < vals.basis_values.size(); ++i)
				{
					const auto &vv = vals.basis_values[i];
					assert(vv.val.size() == weights.size());
					const double b_val = vv.val(p);

					for (int d = 0; d < size_; ++d)
					{
						for (std::size_t ii = 0; ii < vv.global.size(); ++ii)
						{
							local_displacement(d) += (vv.global[ii].val * b_val) * displacement(vv.global[ii].index * size_ + d);
						}
					}
				}

				for (int d = 0; d < size_; ++d)
					res -= forces(p, d) * local_displacement(d) * weights(p);
			}
		}

		return res;
	}

	bool RhsAssembler::boundary_quadrature(const LocalBoundary &local_boundary, const int order, const bool skip_computation, Eigen::MatrixXd &uv, Eigen::MatrixXd &points, Eigen::MatrixXd &normals, Eigen::VectorXd &weights, Eigen::VectorXi &global_primitive_ids) const
	{
		uv.resize(0, 0);
		points.resize(0, 0);
		normals.resize(0, 0);
		weights.resize(0);
		global_primitive_ids.resize(0);

		for (int i = 0; i < local_boundary.size(); ++i)
		{
			const int gid = local_boundary.global_primitive_id(i);
			Eigen::MatrixXd tmp_p, tmp_uv, tmp_n;
			Eigen::VectorXd tmp_w;
			switch (local_boundary.type())
			{
			case BoundaryType::TriLine:
				BoundarySampler::quadrature_for_tri_edge(local_boundary[i], order, gid, mesh_, tmp_uv, tmp_p, tmp_w);
				BoundarySampler::normal_for_tri_edge(local_boundary[i], tmp_n);
				break;
			case BoundaryType::QuadLine:
				BoundarySampler::quadrature_for_quad_edge(local_boundary[i], order, gid, mesh_, tmp_uv, tmp_p, tmp_w);
				BoundarySampler::normal_for_quad_edge(local_boundary[i], tmp_n);
				break;
			case BoundaryType::Quad:
				BoundarySampler::quadrature_for_quad_face(local_boundary[i], order, gid, mesh_, tmp_uv, tmp_p, tmp_w);
				BoundarySampler::normal_for_quad_face(local_boundary[i], tmp_n);
				break;
			case BoundaryType::Tri:
				BoundarySampler::quadrature_for_tri_face(local_boundary[i], order, gid, mesh_, tmp_uv, tmp_p, tmp_w);
				BoundarySampler::normal_for_tri_face(local_boundary[i], tmp_n);
				break;
			case BoundaryType::Polygon:
				BoundarySampler::quadrature_for_polygon_edge(local_boundary.element_id(), local_boundary.global_primitive_id(i), order, mesh_, tmp_uv, tmp_p, tmp_w);
				BoundarySampler::normal_for_polygon_edge(local_boundary.element_id(), local_boundary.global_primitive_id(i), mesh_, tmp_n);
				break;
			case BoundaryType::Invalid:
				assert(false);
				break;
			default:
				assert(false);
			}

			uv.conservativeResize(uv.rows() + tmp_uv.rows(), tmp_uv.cols());
			uv.bottomRows(tmp_uv.rows()) = tmp_uv;

			points.conservativeResize(points.rows() + tmp_p.rows(), tmp_p.cols());
			points.bottomRows(tmp_p.rows()) = tmp_p;

			normals.conservativeResize(normals.rows() + tmp_p.rows(), tmp_p.cols());
			for (int k = normals.rows() - tmp_p.rows(); k < normals.rows(); ++k)
				normals.row(k) = tmp_n;

			weights.conservativeResize(weights.rows() + tmp_w.rows(), tmp_w.cols());
			weights.bottomRows(tmp_w.rows()) = tmp_w;

			global_primitive_ids.conservativeResize(global_primitive_ids.rows() + tmp_p.rows());
			global_primitive_ids.bottomRows(tmp_p.rows()).setConstant(gid);
		}

		assert(uv.rows() == global_primitive_ids.size());
		assert(points.rows() == global_primitive_ids.size());
		assert(normals.rows() == global_primitive_ids.size());
		assert(weights.size() == global_primitive_ids.size());

		return true;
	}

	bool RhsAssembler::sample_boundary(const LocalBoundary &local_boundary, const int n_samples, const bool skip_computation, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples, Eigen::VectorXi &global_primitive_ids) const
	{
		uv.resize(0, 0);
		samples.resize(0, 0);
		global_primitive_ids.resize(0);

		for (int i = 0; i < local_boundary.size(); ++i)
		{
			Eigen::MatrixXd tmp, tmp_uv;
			switch (local_boundary.type())
			{
			case BoundaryType::TriLine:
				BoundarySampler::sample_parametric_tri_edge(local_boundary[i], n_samples, tmp_uv, tmp);
				break;
			case BoundaryType::QuadLine:
				BoundarySampler::sample_parametric_quad_edge(local_boundary[i], n_samples, tmp_uv, tmp);
				break;
			case BoundaryType::Quad:
				BoundarySampler::sample_parametric_quad_face(local_boundary[i], n_samples, tmp_uv, tmp);
				break;
			case BoundaryType::Tri:
				BoundarySampler::sample_parametric_tri_face(local_boundary[i], n_samples, tmp_uv, tmp);
				break;
			case BoundaryType::Polygon:
				BoundarySampler::sample_polygon_edge(local_boundary.element_id(), local_boundary.global_primitive_id(i), n_samples, mesh_, tmp_uv, tmp);
				break;
			case BoundaryType::Invalid:
				assert(false);
				break;
			default:
				assert(false);
			}

			uv.conservativeResize(uv.rows() + tmp_uv.rows(), tmp_uv.cols());
			uv.bottomRows(tmp_uv.rows()) = tmp_uv;

			samples.conservativeResize(samples.rows() + tmp.rows(), tmp.cols());
			samples.bottomRows(tmp.rows()) = tmp;

			global_primitive_ids.conservativeResize(global_primitive_ids.rows() + tmp.rows());
			global_primitive_ids.bottomRows(tmp.rows()).setConstant(local_boundary.global_primitive_id(i));
		}

		assert(uv.rows() == global_primitive_ids.size());
		assert(samples.rows() == global_primitive_ids.size());

		return true;
	}

} // namespace polyfem


/*=============================================================================
Copyright (c) 2016 Paul W. Bible

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

#include <ggtk/TermInformationContentMap.hpp>
#include <ggtk/SharedInformationInterface.hpp>
#include <ggtk/GoGraph.hpp>
#include <ggtk/SetUtilities.hpp>
#include <ggtk/Accumulators.hpp>

#include <boost/unordered_map.hpp>
#include <boost/accumulators/statistics/max.hpp>

/*! \class MICASharedInformation
	\brief A class to calculate shared infromation as the most informative common ancestor (MICA)

	This class calculates shared infromation using the most informative common ancestor (MICA).
	 The MICA is a term that is also known as the minimum subsumer.

	 This shared information method forms the basis of 3 inforamtion content measures
	 put forward by Lord el al.

    P. W. Lord, R. D. Stevens, A. Brass, and C. A. Goble,
	 "Semantic similarity measures as tools for exploring the gene ontology,"
	 Pac Symp Biocomput, pp. 601-12, 2003.

*/
class MICASharedInformation : public SharedInformationInterface{

public:
	//! A constructor
	/*!
		Creates the MICASharedInformation class
	*/
	inline MICASharedInformation(GoGraph* goGraph,TermInformationContentMap &icMap){
		_goGraph = goGraph;
		_icMap = icMap;
	}

	//! A method for calculating the shared infromation between two concepts.
	/*!
		This method returns the shared information between two concepts.
	*/
	inline double sharedInformation(const std::string &termA,const std::string &termB){
		// return 0 for any terms not in the datbase
		if (!_icMap.hasTerm(termA) || !_icMap.hasTerm(termB)){
			return 0.0;
		}
		// return 0 for terms in different ontologies
		if (_goGraph->getTermOntology(termA) != _goGraph->getTermOntology(termB)){
			return 0.0;
		}

		Accumulators::MaxAccumulator myMax;

		boost::unordered_set<std::string> ancestorsA = _goGraph->getAncestorTerms(termA);
		ancestorsA.insert(termA);
		boost::unordered_set<std::string> ancestorsB = _goGraph->getAncestorTerms(termB);
		ancestorsB.insert(termB);

		boost::unordered_set<std::string> sharedAncestors = SetUtilities::set_intersection(ancestorsA,ancestorsB);

		boost::unordered_set<std::string>::iterator iter = sharedAncestors.begin();
		for(;iter != sharedAncestors.end(); ++iter){
			myMax(_icMap[*iter]);
		}

		return Accumulators::extractMax(myMax);
	}

	//! An interface method for returning the shared information of a single terms,or information content
	/*!
		This method privdes a mechanism for returing a term's infromation content.
	*/
	inline double sharedInformation(const std::string &term){
		// return 0 for any terms not in the datbase
		if (!_icMap.hasTerm(term)){
			return 0.0;
		}
		return _icMap[term];
	}

	//! An interface method for returning the maximum information content for a term
	/*!
		This method provides the absolute max information content within a corpus for normalization purposes.
	*/
	inline double maxInformationContent(const std::string &term){

		double maxIC;

		//select the correct ontology normalization factor
		GO::Onto ontoType = _goGraph->getTermOntology(term);
		if(ontoType == GO::BP){
			maxIC = -std::log(_icMap.getMinBP());
		}else if(ontoType == GO::MF){
			maxIC = -std::log(_icMap.getMinMF());
		}else{
			maxIC = -std::log(_icMap.getMinCC());
		}

		return maxIC;
	}

	//! An interface method for determining if a term can be found
	/*!
		Determines if the term can be found in the current map.
	*/
	inline bool hasTerm(const std::string &term){
		return _icMap.hasTerm(term);
	}

	//! An interface method for determining if the two terms are of like ontologies.
	/*!
	Determine if two terms are of the same ontology.
	*/
	bool isSameOntology(const std::string &termA, const std::string &termB){
		return _goGraph->getTermOntology(termA) == _goGraph->getTermOntology(termB);
	}

private:

	GoGraph* _goGraph;
	TermInformationContentMap _icMap;

};
#endif


/* Copyright Institue of Sound and Vibration Research - All rights reserved. */

#include <libvisr/parameter_factory.hpp>
#include <libvisr/parameter_type.hpp>

#include <libpml/matrix_parameter.hpp>
#include <libpml/matrix_parameter_config.hpp>

#include <boost/test/unit_test.hpp>

#include <iostream>

namespace visr
{
namespace ril
{
namespace test
{

// That needs to go to pml
#if 0
BOOST_AUTO_TEST_CASE( CheckTypePresent )
{
  std::size_t numRows = 3;
  std::size_t numCols = 7;

  pml::MatrixParameterConfig const mtxCfg{ numRows, numCols };

  auto const paramPtr = ParameterFactory::create( pml::MatrixParameter<float>::staticType(), mtxCfg );
  BOOST_CHECK( paramPtr );

  pml::MatrixParameter<float> const * mtxParamPtr; // { nullptr };
  BOOST_CHECK_NO_THROW( mtxParamPtr = dynamic_cast<pml::MatrixParameter<float> const *>( paramPtr.get() ) );
  BOOST_CHECK( mtxParamPtr->numberOfRows() == numRows );
}
#endif

} // namespace test
} // namespace ril
} // namespace visr


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

// authors: Hugo Théveniaut and Fabien Alet

#ifndef NETKET_CUSTOMSAMPLER_HPP
#define NETKET_CUSTOMSAMPLER_HPP

#include <mpi.h>
#include <Eigen/Dense>
#include <iostream>
#include <limits>
#include "Operator/local_operator.hpp"
#include "Utils/parallel_utils.hpp"
#include "Utils/random_utils.hpp"
#include "abstract_sampler.hpp"

namespace netket {

// Metropolis sampling using custom moves provided by user
class CustomSampler : public AbstractSampler {
  AbstractMachine &psi_;
  const AbstractHilbert &hilbert_;
  LocalOperator move_operators_;
  std::vector<double> operatorsweights_;

  // number of visible units
  const int nv_;

  // states of visible units
  Eigen::VectorXd v_;

  Eigen::VectorXd accept_;
  Eigen::VectorXd moves_;

  int mynode_;
  int totalnodes_;

  // Look-up tables
  typename AbstractMachine::LookupType lt_;

  std::vector<std::vector<int>> tochange_;
  std::vector<std::vector<double>> newconfs_;
  std::vector<Complex> mel_;

  int nstates_;
  std::vector<double> localstates_;

 public:
  CustomSampler(AbstractMachine &psi, const LocalOperator &move_operators,
                const std::vector<double> &move_weights = {})
      : psi_(psi),
        hilbert_(psi.GetHilbert()),
        move_operators_(move_operators),
        nv_(hilbert_.Size()) {
    Init(move_weights);
  }

  void Init(const std::vector<double> &move_weights) {
    CheckMoveOperators(move_operators_);

    if (hilbert_.Size() != move_operators_.GetHilbert().Size()) {
      throw InvalidInputError(
          "Move operators in CustomSampler act on a different hilbert space "
          "than the Machine");
    }

    if (move_weights.size()) {
      operatorsweights_ = move_weights;

      if (operatorsweights_.size() != move_operators_.Size()) {
        throw InvalidInputError(
            "The custom sampler definition is inconsistent (between "
            "MoveWeights and MoveOperators sizes)");
      }
    } else {  // By default the stochastic operators are drawn uniformly
      operatorsweights_.resize(move_operators_.Size(), 1.0);
    }

    v_.resize(nv_);

    MPI_Comm_size(MPI_COMM_WORLD, &totalnodes_);
    MPI_Comm_rank(MPI_COMM_WORLD, &mynode_);

    if (!hilbert_.IsDiscrete()) {
      throw InvalidInputError(
          "Custom Metropolis sampler works only for discrete Hilbert spaces");
    }

    accept_.resize(1);
    moves_.resize(1);

    nstates_ = hilbert_.LocalSize();
    localstates_ = hilbert_.LocalStates();

    Reset(true);

    InfoMessage() << "Custom Metropolis sampler is ready " << std::endl;
  }

  void Reset(bool initrandom = false) override {
    if (initrandom) {
      hilbert_.RandomVals(v_, this->GetRandomEngine());
    }

    psi_.InitLookup(v_, lt_);

    accept_ = Eigen::VectorXd::Zero(1);
    moves_ = Eigen::VectorXd::Zero(1);
  }

  void Sweep() override {
    std::discrete_distribution<int> disc_dist(operatorsweights_.begin(),
                                              operatorsweights_.end());
    std::uniform_real_distribution<double> distu;

    for (int i = 0; i < nv_; i++) {
      // pick a random operator in possible ones according to the provided
      // weights
      int op = disc_dist(this->GetRandomEngine());

      move_operators_.FindConn(op, v_, mel_, tochange_, newconfs_);

      double p = distu(this->GetRandomEngine());
      std::size_t exit_state = 0;
      double cumulative_prob = std::real(mel_[0]);
      while (p > cumulative_prob) {
        exit_state++;
        cumulative_prob += std::real(mel_[exit_state]);
      }

      double ratio = std::norm(std::exp(psi_.LogValDiff(
          v_, tochange_[exit_state], newconfs_[exit_state], lt_)));

      // Metropolis acceptance test
      if (ratio > distu(this->GetRandomEngine())) {
        accept_[0] += 1;
        psi_.UpdateLookup(v_, tochange_[exit_state], newconfs_[exit_state],
                          lt_);
        hilbert_.UpdateConf(v_, tochange_[exit_state], newconfs_[exit_state]);
      }
      moves_[0] += 1;
    }
  }

  Eigen::VectorXd Visible() override { return v_; }

  void SetVisible(const Eigen::VectorXd &v) override { v_ = v; }

  AbstractMachine &GetMachine() noexcept override { return psi_; }

  const AbstractHilbert &GetHilbert() const noexcept override {
    return hilbert_;
  }

  Eigen::VectorXd Acceptance() const override {
    Eigen::VectorXd acc = accept_;
    for (int i = 0; i < 1; i++) {
      acc(i) /= moves_(i);
    }
    return acc;
  }

  static void CheckMoveOperators(const LocalOperator &move_operators) {
    if (move_operators.Size() == 0) {
      throw InvalidInputError("No valid MoveOperators provided");
    }

    const auto local_matrices = move_operators.LocalMatrices();
    const auto acting_on = move_operators.ActingOn();

    std::set<int> touched_sites;
    for (std::size_t c = 0; c < local_matrices.size(); c++) {
      // check if matrix of this operator is stochastic
      bool is_stochastic = true;
      bool is_complex = false;
      bool is_definite_positive = true;
      bool is_symmetric = true;
      bool is_offdiagonal = true;

      const double epsilon = 1.0e-6;
      double sum_diagonal = 0;
      for (std::size_t i = 0; i < local_matrices[c].size(); i++) {
        double sum_column = 0.;
        for (std::size_t j = 0; j < local_matrices[c].size(); j++) {
          if (std::abs(std::imag(local_matrices[c][i][j])) > epsilon) {
            is_complex = true;
            is_stochastic = false;
            break;
          }
          if (std::real(local_matrices[c][i][j]) < 0) {
            is_definite_positive = false;
            is_stochastic = false;
            break;
          }
          if (std::abs(local_matrices[c][i][j] - local_matrices[c][j][i]) >
              epsilon) {
            is_symmetric = false;
            is_stochastic = false;
            break;
          }
          sum_column += std::real(local_matrices[c][i][j]);
        }
        if (std::abs(sum_column - 1.) > epsilon) {
          is_stochastic = false;
        }
        sum_diagonal += std::real(local_matrices[c][i][i]);
      }
      is_offdiagonal =
          std::abs(sum_diagonal - local_matrices[c].size()) > epsilon;
      if (is_offdiagonal == false) {
        throw InvalidInputError("MoveOperators has a diagonal move operator");
      }
      if (is_complex == true) {
        throw InvalidInputError("MoveOperators has complex matrix elements");
      }
      if (is_definite_positive == false) {
        throw InvalidInputError("MoveOperators has negative matrix elements");
      }
      if (is_symmetric == false) {
        throw InvalidInputError("MoveOperators is not symmetric");
      }
      if (is_stochastic == false) {
        throw InvalidInputError("MoveOperators is not stochastic");
      }

      for (std::size_t i = 0; i < acting_on[c].size(); i++) {
        touched_sites.insert(acting_on[c][i]);
      }
    }

    if (static_cast<int>(touched_sites.size()) !=
        move_operators.GetHilbert().Size()) {
      InfoMessage() << "Warning: MoveOperators appear not to act on "
                       "all sites of the space:"
                    << std::endl;
      InfoMessage() << "Check ergodicity" << std::endl;
    }
  }
};
}  // namespace netket

#endif


#include <complex>
#include <exception>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <fmt/format.h>
//#include <boost/config/warning_disable.hpp>
//#include <boost/spirit/home/x3.hpp>
//#include <range/v3/all.hpp>
#include <cstdint>
// 0 ,1
//1,2
//(i,j)( i+j +1
//i,j+1   (i+j+1,0
int main(int argc, char **argv)
{
  int i = 2978 - 1, j = 3083 - 1;
  int n = (i - 1) + j;
  int total = 0;
  for (int i = 0; i <= n; ++i) {
    total += 1 + i;
  }
  total += j + 1;
  //a[total]-1;
  std::int64_t a = 20151125;
  for (int i = 1; i < total; ++i) {
    a *= 252533;
    a %= 33554393;
    //fmt::print(";i:{},a:{}", i, a);
    //
  }
  fmt::print("{}", a);
}