/*  This file is part of libDAI - http://www.libdai.org/
 *
 *  Copyright (c) 2006-2011, The libDAI authors. All rights reserved.
 *
 *  Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
 */

#include <dai/factorgraph.h>
#include <iostream>
#include <fstream>
#include <dai/util.h>
#include <dai/alldai.h>
#include <fstream>
#include "dai/emrun.h"
#include <dai/util.h>
#include <string>
#include <sys/stat.h>
#include <time.h>
#include <iomanip>      // std::setprecision
#include <sstream>
#include <cstdlib>
#include <algorithm>
#include<math.h>
#include <string>
#include <iostream>

#include <boost/random/uniform_real.hpp>
#include <boost/random/variate_generator.hpp>
using namespace std;
using namespace dai;
// Define a random number generator and initialize it with a reproducible
// seed.
mt19937 generator(42);

// Define a uniform random number distribution which produces "double"
// values between 0 and 1 (0 inclusive, 1 exclusive).
boost::uniform_real<> uni_dist(0,1);
boost::variate_generator<mt19937&, boost::uniform_real<> > uni(generator, uni_dist);
int main() {
    // This example program illustrates how to construct a factorgraph
    // by means of the sprinkler network example discussed at
    // http://www.cs.ubc.ca/~murphyk/Bayes/bnintro.html

    Var C(0, 2);  // Define binary variable Cloudy (with label 0)
    Var S(1, 2);  // Define binary variable Sprinkler (with label 1)
    Var R(2, 2);  // Define binary variable Rain (with label 2)
    Var W(3, 2);  // Define binary variable Wetgrass (with label 3)

    // Define probability distribution for C
    Factor P_C( C );
    P_C.set(0, 0.5);   // C = 0
    P_C.set(1, 0.5);   // C = 1

    // Define conditional probability of R given C
    Factor P_R_given_C( VarSet( R, C ) );
    P_R_given_C.set(0, 0.8);   // C = 0, R = 0
    P_R_given_C.set(1, 0.2);   // C = 1, R = 0
    P_R_given_C.set(2, 0.2);   // C = 0, R = 1
    P_R_given_C.set(3, 0.8);   // C = 1, R = 1

    // Define conditional probability of S given C
    Factor P_S_given_C( VarSet( S, C ) );
    P_S_given_C.set(0, 0.5);   // C = 0, S = 0
    P_S_given_C.set(1, 0.9);   // C = 1, S = 0
    P_S_given_C.set(2, 0.5);   // C = 0, S = 1
    P_S_given_C.set(3, 0.1);   // C = 1, S = 1

    // Define conditional probability of W given S and R
    Factor P_W_given_S_R( VarSet( S, R ) | W );
    P_W_given_S_R.set(0, 1.0);  // S = 0, R = 0, W = 0
    P_W_given_S_R.set(1, 0.1);  // S = 1, R = 0, W = 0
    P_W_given_S_R.set(2, 0.1);  // S = 0, R = 1, W = 0
    P_W_given_S_R.set(3, 0.01); // S = 1, R = 1, W = 0
    P_W_given_S_R.set(4, 0.0);  // S = 0, R = 0, W = 1
    P_W_given_S_R.set(5, 0.9);  // S = 1, R = 0, W = 1
    P_W_given_S_R.set(6, 0.9);  // S = 0, R = 1, W = 1
    P_W_given_S_R.set(7, 0.99); // S = 1, R = 1, W = 1

    // Build factor graph consisting of those four factors
    vector<Factor> SprinklerFactors;
    SprinklerFactors.push_back( P_C );
    SprinklerFactors.push_back( P_S_given_C );
    SprinklerFactors.push_back( P_R_given_C );
    SprinklerFactors.push_back( P_W_given_S_R );
    FactorGraph SprinklerNetwork( SprinklerFactors );
    FactorGraph SprinklerNetwork1( SprinklerFactors );
    // Write factorgraph to a file
    SprinklerNetwork.WriteToFile( "sprinkler.fg" );
    cout << "Sprinkler network written to sprinkler.fg" << endl;

    // Output some information about the factorgraph
    cout << SprinklerNetwork.nrVars() << " variables" << endl;
    cout << SprinklerNetwork.nrFactors() << " factors" << endl;

 //Code for mutation
    for( size_t I = 0; I < SprinklerNetwork.nrFactors(); I++ )
    {
        int no_states = SprinklerNetwork.factor( I ).nrStates();// total no. of states
        int no_arr_arrset =0; // factor_states
        int arr_size =1; //states of parents before factor_pos
        int arr_set =0; //states of parents after factor_pos
        int arr_set_states =0; // no. of states till factor_pos

        cout<<SprinklerNetwork.factor( I ).nrStates()<<endl;
        Factor P = SprinklerNetwork.factor( I );
        VarSet vs = P.vars();
        cout<<"vars = "<<vs<<endl;
        cout << "{";
        for( VarSet::const_iterator v = vs.begin(); v != vs.end(); v++ )
        {
            cout << (v != vs.begin() ? ", " : "") << *v;
            if(v->label() == I)
            {
                cout<<" states = "<<v->states()<<endl;
                no_arr_arrset = v->states();
                arr_set_states = arr_size * v->states();
                break;
            }
            arr_size = arr_size * v->states();
        }
        cout << "}"<<endl;
        arr_set = no_states/arr_set_states;
        dai::TProb<double> myvector = P.p();

        for (dai::TProb<double>::iterator it = myvector.begin();it != myvector.end(); ++it)
            std::cout << ' ' << *it;
        std::cout << '\n';
//        cout<<"no. of states "<<no_states<<"\t"<<"array set "<<arr_set<<"\t"<<"no_arr_arrset "<<no_arr_arrset<<"\t"<<"array size "<<arr_size<<endl;
        int k=0;
        while(k<arr_set)
        {
            //Random values are generated---~----~----
            std::vector< double * > Arrays1;
            for(int l=0; l<no_arr_arrset; l++)
            {
                Arrays1.push_back( new double[arr_size]);
            }
            vector<double*>::iterator it;
            for(int m=0; m<arr_size; m++)
            {
                it=Arrays1.begin();
                std::vector<double> vec;
                for(int i=0; i<no_arr_arrset; i++)
                {
                    vec.push_back(uni());
                }
                const double total = std::accumulate(vec.begin(), vec.end(), 0.0);
                for (double& value: vec)
                {
                    double* arr = *it;
                    value /= total;
                    arr[m] = value;
                    ++it;
                    //cout<<value <<endl;
                }
                // cout<<endl;
                vec.clear();
            }
            //Random values are generated---x----x----
            //Random or actual values will be updated -----~------~
            vector<double*>::iterator it11;
            it11=Arrays1.begin();
            int stat=0;
            for(int l=0; l<no_arr_arrset; l++)
            {
                double* arr = *it11;
                for(int m=0; m<arr_size; m++)
                {
                    stringstream v;
                    v << fixed << setprecision(12) << arr[m];
                    //c1<<stat<<"   "<<v.str()<<endl;
                    //cout<< "replaced"<<v.str()<<endl;
                    P.set(stat,arr[m]);
                    stat++;
                }
                ++it11;
            }
            //Random values are updated ------x------x--------
            k++;
        }

        SprinklerNetwork.setFactor(I,P);
        dai::TProb<double> myvector1 = P.p();

        for (dai::TProb<double>::iterator it1 = myvector1.begin() ; it1 != myvector1.end(); ++it1)
            std::cout << ' ' << *it1;
        std::cout << '\n';

    }
    // Code for crossover

    int population_size=2;
    // compute crossover probability
    double pcross = uni(); // generates random a value between 0 and 1
	cout<<"Crossover probability "<<pcross;
	// 4. Select 2 parents randomly
	const int LOW = 0;
	const int HIGH = population_size-1;
    set<int> myset;
	set<int>::iterator it;
    int *arrval;
	size_t p=0;
	arrval = new int[population_size];
    /*Declare variable to hold seconds on clock.*/
	time_t seconds;
	/*Get value from system clock and place in seconds variable.*/
	time(&seconds);
	/*Convert seconds to a unsigned integer.*/
	srand(time(0));
    for( size_t n = 0; n < population_size; n++ )
	{
        int val = rand() % (HIGH - LOW + 1) + LOW;
        it=myset.find(val);
        arrval[n] = val;
        if(it==myset.end()) // if the value is not in the set
        {
            myset.insert(val);
            p++;
            if(p == 2)
            {
                cout <<" Random value is"<<arrval[n]<<" and "<< arrval[n-1];
                // randomly select a crossover point
                int cr_pt = (rand() % SprinklerNetwork.nrVars()) +1;
                int c = 0; // count of parameters
                int p_c1 = 0, p_c2 =0; // counter to skip the first four config lines in the cpt of the fg file
                cout<<" Crossover point is "<< cr_pt<<endl;
                p=0;
                for( size_t I = cr_pt; I < SprinklerNetwork.nrFactors(); I++ )
                {
                    Factor P = SprinklerNetwork.factor( I );
                    dai::TProb<double> myvector = P.p();
                    Factor P1 = SprinklerNetwork1.factor( I );
                    dai::TProb<double> myvector1 = P1.p();
                    dai::TProb<double>::iterator it1 = myvector1.begin();
                    dai::TProb<double>::iterator it = myvector.begin();
                    for (int state=0;state<myvector.size();state++)
                    {
                        P.set(state,*it1);
                        P1.set(state,*it);
                        ++it1;++it;
                    }
                    SprinklerNetwork.setFactor(I,P);
                    SprinklerNetwork1.setFactor(I,P1);
                }
            }
        }
        else
        {
            n=n-1;
            continue;
        }
    }
    // Calculate joint probability of all four variables
    Factor P2;
    for( size_t I = 0; I < SprinklerNetwork.nrFactors(); I++ )
    {
        cout<<SprinklerNetwork.factor( I ).nrStates()<<endl;
        P2 = SprinklerNetwork.factor( I );
        dai::TProb<double> myvector = P2.p();

        for (dai::TProb<double>::iterator it = myvector.begin() ; it != myvector.end(); ++it)
            std::cout << ' ' << *it;
        std::cout << '\n';
    }
    std::cout << '\n';
    Factor P3;
    for( size_t I = 0; I < SprinklerNetwork1.nrFactors(); I++ )
    {
        cout<<SprinklerNetwork1.factor( I ).nrStates()<<endl;
        P3 = SprinklerNetwork1.factor( I );
        dai::TProb<double> myvector = P3.p();

        for (dai::TProb<double>::iterator it = myvector.begin() ; it != myvector.end(); ++it)
            std::cout << ' ' << *it;
        std::cout << '\n';
    }
//        P.randomize();
//        P.normalized();
//        dai::TProb<double> myvector1 = P.p();
//
//        for (dai::TProb<double>::iterator it1 = myvector1.begin() ; it1 != myvector1.end(); ++it1)
//            std::cout << ' ' << *it1;
        std::cout << '\n';
//        VarSet vs = P.vars();
//        cout<<"vars = "<<vs<<endl;
//        cout << "{";
//        for( VarSet::const_iterator v = vs.begin(); v != vs.end(); v++ )
//        {
//            cout << (v != vs.begin() ? ", " : "") << *v;
////            std::string result,result1;
////            result = name + boost::lexical_cast<std::string>(I);
////            result1 = ""+boost::lexical_cast<std::string>(*v);
////            cout<<" factor = "<<result<<" "<<v->label()<<endl;
//
//            if(v->label() == I)
//            {
//                cout<<" states = "<<v->states()<<endl;
//            }
//        }
//        cout << "}"<<endl;
        //size_t f =SprinklerNetwork.findVar(v);
        //cout<<"factor index "<<f<<endl;
       // P *= SprinklerNetwork.factor( I );
//    }// P.normalize();  // Not necessary: a Bayesian network is already normalized by definition
//    // Calculate some probabilities
//    Real denom = P.marginal( W )[1];
//    //VarSet v = P.vars();
//    //cout<<"vars = "<<v<<endl;
//
//    //for( size_t n = 0; n < v.nrStates(); X_n++ )
//        //cout<<" vars inside "<<*X_n<<endl;
//    //cout<<P.marginal( *X_n )<<" "<< P.marginal( W )[1]<<endl;
//    cout<<P.normalize()<<endl;
//    cout << "P(W=1) = " << denom << endl;
//    cout << "P(S=1 | W=1) = " << P.marginal( VarSet( S, W ) )[3] / denom << endl;
//    cout << "P(R=1 | W=1) = " << P.marginal( VarSet( R, W ) )[3] / denom << endl;
//
    return 0;
}


/*! \file numeric_limits_handling.hpp
    \brief Functions to check if data values are NaN or infinity or denormalised.
    \details
      Since only double is used, template versions are not needed,
      and TR1 should provide max, min, denorm_min, infinity and isnan,
      but older compilers and libraries may not provide all these.

      Better to use boost::math throughout?  Done?

    \author Jacob Voytko and Paul A. Bristow
*/

// Copyright Jacob Voytko 2007
// Copyright Paul A. Bristow 2007, 2013

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

#ifndef BOOST_SVG_NUMERIC_LIMITS_HANDLING_DETAIL_HPP
#define BOOST_SVG_NUMERIC_LIMITS_HANDLING_DETAIL_HPP

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

#include <boost/quan/unc.hpp>
// using boost::quan::uncun ...
#include <boost/quan/meas.hpp>
// using boost::quan Meas;

#include <limits>
  // using std::numeric_limits;
#include <cmath> // Why?

namespace boost
{
namespace svg
{
namespace detail
{
  // Provide checks on data values to be plotted.
  // Test if at max or +infinity, or -max or - infinity, or NaN.

// std::numeric_limits<double>::min() or denorm min() are just ignored as almost zero (which is an OK value).

constexpr double margin = 4.;  //!< Consider values close to std::numeric_limits<double>::max() as maximum to try to take account of computation errors.

using boost::quan::unc;
using boost::quan::Meas;

inline bool limit_max(double a)
{ //! At (or near) max value or +infinity, most positive values.
    return ((a > (std::numeric_limits<double>::max)() / margin) // Avoid macro max trap!
         || (std::isinf(a)));
        // || (a == std::numeric_limits<double>::infinity()));
}

inline bool limit_min(double a)
{ //! At (or near) -max or -infinity, most negative values.
  return (
    (a < -(std::numeric_limits<double>::max)() /margin) // Avoid macro max trap!
    || (a == -std::numeric_limits<double>::infinity())
    );
}

// Allow NaNs to be displayed differently from just too big or too small values.
inline bool limit_NaN(double a)
{ //! Separate test for NaNs.
  using std::isnan;
  return isnan(a) ? true : false;
  // Ternary operator used to remove warning about casting int to bool.
}

inline bool is_limit(double a)
{ //! Is at some limit.
  return limit_max(a) || limit_min(a) || limit_NaN(a);
}

inline bool pair_is_limit(std::pair<const double, double> a)
{ //! Check on both x and y double data points. Return false if either or both are at limit.
  return limit_max(a.first) || limit_min(a.first) || limit_NaN(a.first)
    || limit_max(a.second) || limit_min(a.second) || limit_NaN(a.second);
}

inline bool pair_is_limit(std::pair<double, double> a)
{ //! Check on both x and y double data points. Return false if either or both are at limit.
  return limit_max(a.first) || limit_min(a.first) || limit_NaN(a.first)
    || limit_max(a.second) || limit_min(a.second) || limit_NaN(a.second);
}

inline bool pair_is_limit(std::pair<const int, double> a)
{ //! Check on both x int and y double data points. Return false if either or both are at limit.
  return limit_max(a.first) || limit_min(a.first) // || limit_NaN(a.first) - can't be NaN if int.
    || limit_max(a.second) || limit_min(a.second) || limit_NaN(a.second);
}

inline bool pair_is_limit(std::pair<int, double> a)
{ //! Check on both x int and y double data points. Return false if either or both are at limit.
  return limit_max(a.first) || limit_min(a.first) // || limit_NaN(a.first) - can't be NaN if int.
    || limit_max(a.second) || limit_min(a.second) || limit_NaN(a.second);
}

template <bool correlated>
inline bool pair_is_limit(std::pair<const unc<correlated>, unc<correlated> > a)
{ //! Check on values of both x and y unc data points.
  // \return false if either or both are at limit.
  return limit_max(value_of(a.first)) || limit_min(value_of(a.first)) || limit_NaN(value_of(a.first))
    || limit_max(value_of(a.second)) || limit_min(value_of(a.second)) || limit_NaN(value_of(a.second));
}

template <bool correlated>
inline bool pair_is_limit(std::pair<Meas, unc<correlated> > a) // const version
{ //! Check on values of both x Meas and y unc data points.
  // \return false if either or both are at limit.
  return limit_max(value_of(a.first)) || limit_min(value_of(a.first)) || limit_NaN(value_of(a.first))
    || limit_max(value_of(a.second)) || limit_min(value_of(a.second)) || limit_NaN(value_of(a.second));
}

template <bool correlated>
inline bool pair_is_limit(std::pair<const Meas, unc<correlated> > a)
{ //! Check on values of both x Meas and y unc data points.
  // \return false if either or both are at limit.
    double rounddown2(double value); // 2, 4,
  return limit_max(value_of(a.first)) || limit_min(value_of(a.first)) || limit_NaN(value_of(a.first))
    || limit_max(value_of(a.second)) || limit_min(value_of(a.second)) || limit_NaN(value_of(a.second));
}

} // namespace detail
} // namespace svg
} // namespace boost

// Defines :
/*
bool boost::svg::detail::limit_max(double); // true if max or +infinity.
bool boost::svg::detail::limit_min(double); // true if min, denorm_min or -infinity.
bool boost::svg::detail::limit_NaN(double); // true if NaN.
bool boost::svg::detail::is_limit(double); // max, min, infinity or NaN - not a 'proper' data value.
bool boost::svg::detail::pair_is_limit(std::pair<double, double>); // x and/or y  not a proper data value.
bool boost::svg::detail::pair_is_limit(std::pair<const double, double>); // x and/or y  not a proper data value.

template <bool correlated>
bool boost::svg::detail::pair_is_limit(std::pair<const unc<correlated>, unc<correlated> >); // x and/or y not a proper data value!
*/
#endif // BOOST_SVG_NUMERIC_LIMITS_HANDLING_DETAIL_HPP


#ifndef TOOLS_HPP
#define TOOLS_HPP

#include <vector>
#include <Eigen/Dense>

using namespace Eigen;
using namespace std;

double pi();
double deg2rad(double x);
double rad2deg(double x);

double distance(double x1, double y1, double x2, double y2);
double norm(double x, double y);
double norm(double x, double y, double z);
double mph2mps(double mph);
double mps2mph(double mps);

int getLane(const double d, const double laneWidth=4.0);
double getLaneOffsetD(const int lane_number, const double laneWidth=4.0);

vector<double> polyfit(vector<double> &xvals, vector<double> &yvals, int order);
Eigen::VectorXd polyfit(Eigen::VectorXd &xvals, Eigen::VectorXd &yvals, int order);

vector<double> polyeval(vector<double> &coeffs, vector<double> &x);
double polyeval(vector<double> &coeffs, double x);
double polyeval(Eigen::VectorXd coeffs, double x);

vector<double> polyfit_wp(int wp_start, int wp_stop, int order,
						  vector<double> &map_x, vector<double> &map_y);

vector<double> JMT(vector< double> start, vector <double> end, double T);

 //define TOOLS_HPP
 #endif


#include "conex/exponential_map.h"

#include "conex/jordan_matrix_algebra.h"
#include "gtest/gtest.h"

#include <Eigen/Dense>
#include <unsupported/Eigen/MatrixFunctions>

namespace conex {

using Eigen::MatrixXd;
using Eigen::VectorXd;

using JordanTypes = testing::Types<Real, Complex, Quaternions>;

template <typename T>
HyperComplexMatrix RandomOrthogonal(int n) {
  return T::Orthogonalize(T::Random(n, n));
}

template <typename T>
HyperComplexMatrix BuildMatrix(const VectorXd& eigenvalues,
                               const HyperComplexMatrix& Q) {
  int n = eigenvalues.rows();
  assert(n = Q.at(0).rows());
  auto D = T::Zero(n, n);
  D.at(0).diagonal() = eigenvalues;
  return T::Multiply(T::Multiply(Q, D), T::ConjugateTranspose(Q));
}

GTEST_TEST(ExponentialMapPadeApproximation, CompareWithEigen) {
  int n = 4;
  MatrixXd A(n, n);
  A << 3, 1, 0, 1, 1, 3, 1, 0, 0, 1, 4, 1, 1, 0, 1, 5;
  A = A * .001;

  MatrixXd reference = A.exp();
  MatrixXd calculated(n, n);

  HyperComplexMatrix arg(1);
  arg.at(0) = A;
  HyperComplexMatrix result(1);
  ExponentialMap(arg, &result);
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
      EXPECT_NEAR(reference(i, j), result.at(0)(i, j), 1e-7);
    }
  }
}

template <typename T>
class TestCases : public testing::Test {
 public:
  void CompareWithReference() {
    int n = 4;
    VectorXd eigenvalues(n);
    eigenvalues << -.1, .1, .1, .01;
    auto Q = RandomOrthogonal<T>(n);
    auto arg = BuildMatrix<T>(eigenvalues, Q);
    auto reference = BuildMatrix<T>(eigenvalues.array().exp(), Q);

    auto result = T::Zero(n, n);
    ExponentialMap(arg, &result);
    for (size_t k = 0; k < result.size(); k++) {
      for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
          // TODO(FrankPermenter): reduce this threshold.
          EXPECT_NEAR(reference.at(k)(i, j), result.at(k)(i, j), 1e-4);
        }
      }
    }
  }
};

TYPED_TEST_CASE(TestCases, JordanTypes);
TYPED_TEST(TestCases, MultiplyByIdentity) {
  TestFixture::CompareWithReference();
}

template <typename T>
typename T::Matrix Symmetrize(const typename T::Matrix& x) {
  auto y = T::Add(x, T::ConjugateTranspose(x));
  y = T::ScalarMultiply(y, .5);
  return y;
}

GTEST_TEST(TestCases, GeodesicUpdateOctonions) {
  using T = Octonions;
  int order = 3;
  auto s = Symmetrize<T>(T::Random(order, order));
  auto w = T::Identity(order);
  s = T::Add(w, T::ScalarMultiply(s, .1));

  auto y = GeodesicUpdate(w, s);
  EXPECT_TRUE(
      (VectorXd(T::Eigenvalues(s).array().exp()) - T::Eigenvalues(y)).norm() <
      1e-3);

  auto d = T::Zero(order, order);
  for (int i = 0; i < 3; i++) {
    d.at(0)(i, i) = 1 + i * .02;
  }
  y = GeodesicUpdate(T::Identity(order), d);
  for (int i = 0; i < order; i++) {
    EXPECT_NEAR(y.at(0)(i, i), d.at(0).diagonal().array().exp()(i), 1e-4);
  }
}

VectorXd sort(const VectorXd& x) {
  auto y = x;
  std::sort(y.data(), y.data() + x.rows());
  return y;
}

GTEST_TEST(TestCases, GeodesicUpdateRescaling) {
  using T = Octonions;
  int order = 3;
  auto wsqrt = Symmetrize<T>(T::Random(order, order));
  // auto w = T::Multiply(wsqrt, wsqrt);
  auto w = T::Identity(order);
  auto s = Symmetrize<T>(T::Random(order, order));
  s = T::Add(T::Identity(order), T::ScalarMultiply(s, .05));
  s = T::ScalarMultiply(s, -1);

  auto yref = T::ScalarMultiply(GeodesicUpdate(w, s), std::exp(1));
  auto ycalc = GeodesicUpdateScaled(w, s);

  auto eig_ref = T::Eigenvalues(yref);
  auto eig_calc = T::Eigenvalues(ycalc);
  for (int i = 0; i < order; i++) {
    EXPECT_TRUE(eig_calc(i) >= 0);
    EXPECT_NEAR(eig_ref(i), eig_calc(i), 1e-2);
  }
}

}  // namespace conex


// Boost.Geometry (aka GGL, Generic Geometry Library)
// QuickBook Example

// Copyright (c) 2011-2012 Barend Gehrels, Amsterdam, the Netherlands.

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

//[return_envelope
//` Shows how to return the envelope of a ring

#include <iostream>

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/ring.hpp>

#include <boost/assign.hpp>

/*<-*/ #include "create_svg_two.hpp" /*->*/

int main()
{
    using namespace boost::assign;

    typedef boost::geometry::model::d2::point_xy<double> point;

    boost::geometry::model::ring<point> ring;
    ring +=
        point(4.0, -0.5), point(3.5, 1.0),
        point(2.0, 1.5), point(3.5, 2.0),
        point(4.0, 3.5), point(4.5, 2.0),
        point(6.0, 1.5), point(4.5, 1.0),
        point(4.0, -0.5);

    typedef boost::geometry::model::box<point> box;

    std::cout
        << "return_envelope:"
        << boost::geometry::dsv(boost::geometry::return_envelope<box>(ring))
        << std::endl;

    /*<-*/ create_svg("return_envelope.svg", ring, boost::geometry::return_envelope<box>(ring)); /*->*/
    return 0;
}

//]

//[return_envelope_output
/*`
Output:
[pre
return_envelope:((2, -0.5), (6, 3.5))

[$img/algorithms/return_envelope.png]
]
*/
//]


#ifndef HAMILTONIANS_XXZSTO_HPP
#define HAMILTONIANS_XXZSTO_HPP
#include <Eigen/Eigen>
#include <nlohmann/json.hpp>

/* H = XX - \Delta YY - ZZ
 * H = -\Delta XX - YY + ZZ ?
 * */
class XXZSto
{
private:
	int n_;
	double J_;
	double Delta_;

public:

	XXZSto(int n, double J, double Delta)
		: n_(n), J_(J), Delta_(Delta)
	{
	}

	nlohmann::json params() const
	{
		return nlohmann::json
		{
			{"name", "XXZSto"},
			{"n", n_},
			{"J", J_},
			{"Delta", Delta_}
		};
	}

	template<class State>
	typename State::Scalar operator()(const State& smp) const
	{
		typename State::Scalar s = 0.0;
		//Nearest-neighbor
		for(int i = 0; i < n_; i++)
		{
			int zz = smp.sigmaAt(i)*smp.sigmaAt((i+1)%n_);
			s += J_*zz; //zz
			s += J_*(-Delta_+zz)*smp.ratio(i, (i+1)%n_); //xx+yy
		}
		return s;
	}

	std::map<uint32_t, double> operator()(uint32_t col) const
	{
		std::map<uint32_t, double> m;
		for(int i = 0; i < n_; i++)
		{
			int b1 = (col >> i) & 1;
			int b2 = (col >> ((i+1)%n_)) & 1;
			int zz = (1-2*b1)*(1-2*b2);
			long long int x = (1 << i) | (1 << ((i+1)%(n_)));
			m[col] += J_*zz;
			m[col ^ x] += J_*(-Delta_ + zz);
		}
		return m;
	}
};
#endif//HAMILTONIANS_XXZSTO_HPP


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

  @copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_ARCH_COMMON_SCALAR_FUNCTION_COSH_HPP_INCLUDED
#define BOOST_SIMD_ARCH_COMMON_SCALAR_FUNCTION_COSH_HPP_INCLUDED
#include <boost/simd/function/std.hpp>

#include <boost/simd/constant/half.hpp>
#include <boost/simd/constant/log_2.hpp>
#include <boost/simd/detail/constant/maxlog.hpp>
#include <boost/simd/constant/one.hpp>
#include <boost/simd/function/abs.hpp>
#include <boost/simd/function/average.hpp>
#include <boost/simd/function/exp.hpp>
#include <boost/simd/function/rec.hpp>
#include <boost/simd/function/std.hpp>
#include <boost/simd/detail/dispatch/function/overload.hpp>
#include <boost/config.hpp>
#include <cmath>

namespace boost { namespace simd { namespace ext
{
  namespace bd = boost::dispatch;
  namespace bs = boost::simd;
  BOOST_DISPATCH_OVERLOAD ( cosh_
                          , (typename A0)
                          , bd::cpu_
                          , bs::std_tag
                          , bd::scalar_< bd::floating_<A0> >
                          )
  {
    BOOST_FORCEINLINE A0 operator() (const std_tag &,  A0  a0) const BOOST_NOEXCEPT
    {
      return std::cosh(a0);
    }
  };

  BOOST_DISPATCH_OVERLOAD ( cosh_
                          , (typename A0)
                          , bd::cpu_
                          , bd::scalar_<bd::floating_<A0> >
                          )
  {
    BOOST_FORCEINLINE A0 operator() ( A0 const& a0) const BOOST_NOEXCEPT
    {
      //////////////////////////////////////////////////////////////////////////////
      // if x = abs(a0) according x < Threshold e =  exp(x) or exp(x/2) is
      // respectively computed
      // *  in the first case cosh (e+rec(e))/2
      // *  in the second     cosh is (e/2)*e (avoiding undue overflow)
      // Threshold is Maxlog - Log_2
      //////////////////////////////////////////////////////////////////////////////
      A0 x = bs::abs(a0);
      auto test1 = (x > Maxlog<A0>()-Log_2<A0>());
      A0 fac =test1 ? Half<A0>() : One<A0>();
      A0 tmp = exp(x*fac);
      A0 tmp1 = Half<A0>()*tmp;
      return test1 ?tmp1*tmp : bs::average(tmp, rec(tmp));
    }
  };

} } }

#endif


// (C) 2014 Arek Olek

#include <random>
#include <vector>

#include <boost/graph/adjacency_list.hpp>

#include "debug.hpp"
#include "options.hpp"
#include "range.hpp"

template <class Vertex, class Graph>
Vertex random_neighbor(Vertex const & v, Graph const & G) {
  unsigned target = random<unsigned>(0, out_degree(v, G)-1);
  for(auto w : range(adjacent_vertices(v, G)))
    if(target-- == 0) return w;
  assert(false);
}

template <class Graph>
unsigned random_walk(Graph const & G, unsigned v, unsigned t) {
  unsigned steps = 0;
  while(v != t) {
    v = random_neighbor(v, G);
    ++steps;
  }
  return steps;
}

template<class Graph>
void barbell(Graph& G) {
  auto n = num_vertices(G);
  auto m = n / 3;
  for(int i = 0; i < m; ++i) {
    for(int j = i+1; j < m; ++j) {
      add_edge(i, j, G);
      add_edge(i+2*m, j+2*m, G);
    }
    add_edge(i+m, i+m+1, G);
  }
  add_edge(m-1, m, G);
}

boost::adjacency_list<boost::hash_setS, boost::vecS, boost::undirectedS> typedef alist;

int main(int argc, char** argv) {
  options opt(argc, argv);
  const auto z = opt.get<int>("-z", 100);
  auto n = 3*opt.get<int>("-m", 5);
  alist g(n);
  barbell(g);
  std::vector<int> degrees;
  for(auto v : range(vertices(g))) degrees.push_back(degree(v, g));
  std::default_random_engine gen;
  std::discrete_distribution<> pi(degrees.begin(), degrees.end());
  for(int i = 0; i < z; ++i)
    std::cout << n << '\t' << random_walk(g, pi(gen), pi(gen)) << std::endl;
  return 0;
}


#include <boost/test/unit_test.hpp>

#include <scorum/protocol/odds.hpp>

#include "defines.hpp"

#include <scorum/protocol/config.hpp>

#include <limits>

namespace odds_tests {
using namespace scorum;
using namespace scorum::protocol;

BOOST_AUTO_TEST_SUITE(odds_tests)

BOOST_AUTO_TEST_CASE(odds_positive_creation_check)
{
    auto base_fraction = utils::make_fraction(400, 300);
    odds k = base_fraction;

    BOOST_CHECK_EQUAL(k.base(), base_fraction);
    BOOST_CHECK_EQUAL(k.simplified(), utils::make_fraction(4, 3));
    BOOST_CHECK_EQUAL(k.inverted(), utils::make_fraction(4, 1));

    BOOST_CHECK_NO_THROW(odds(utils::make_fraction(std::numeric_limits<int16_t>::max(), 2)));
}

BOOST_AUTO_TEST_CASE(odds_negative_creation_check)
{
    BOOST_REQUIRE_EQUAL(sizeof(odds_value_type), sizeof(uint32_t));

    BOOST_CHECK_NO_THROW(odds(utils::make_fraction(std::numeric_limits<int32_t>::max(), 2)));
    BOOST_CHECK_THROW(odds(utils::make_fraction(std::numeric_limits<uint32_t>::max(), 2)), fc::assert_exception);
    BOOST_CHECK_THROW(odds(utils::make_fraction(std::numeric_limits<int64_t>::max(), 2)), fc::overflow_exception);

    BOOST_CHECK_THROW(odds(utils::make_fraction(-2, 1)), fc::assert_exception);
    BOOST_CHECK_THROW(odds(utils::make_fraction(2, -1)), fc::assert_exception);
    BOOST_CHECK_THROW(odds(utils::make_fraction(-2, -1)), fc::assert_exception);
    BOOST_CHECK_THROW(odds(utils::make_fraction(2, 0)), fc::assert_exception);
    BOOST_CHECK_THROW(odds(utils::make_fraction(0, 1)), fc::assert_exception);
    BOOST_CHECK_THROW(odds(utils::make_fraction(0, 0)), fc::assert_exception);

    BOOST_CHECK_NO_THROW(odds(utils::make_fraction(2, 1)));
}

BOOST_AUTO_TEST_CASE(odds_str_check)
{
    BOOST_CHECK_THROW(odds::from_string(""), fc::exception);
    BOOST_CHECK_THROW(odds::from_string("230"), fc::exception);
    BOOST_CHECK_THROW(odds::from_string("aaaaaaaaa/30"), fc::exception);
    BOOST_CHECK_THROW(odds::from_string("2147483648/1"), fc::exception);
    BOOST_CHECK_THROW(odds::from_string("30/10000000"), fc::exception);

    const std::string str = "30/2";

    odds k = odds::from_string(str);

    BOOST_CHECK_EQUAL(k.simplified(), utils::make_fraction(15, 1));

    BOOST_CHECK_EQUAL(k.to_string(), str);
}

BOOST_AUTO_TEST_CASE(odds_variant_check)
{
    odds k = utils::make_fraction(40, 30);

    fc::variant vk;

    BOOST_REQUIRE_NO_THROW(fc::to_variant(k, vk));

    odds k2;

    BOOST_REQUIRE_NO_THROW(fc::from_variant(vk, k2));

    BOOST_CHECK_EQUAL(k, k2);
}

BOOST_AUTO_TEST_CASE(odds_cast_to_fraction_check)
{
    odds k = utils::make_fraction(40, 30);

    BOOST_CHECK_EQUAL((odds_fraction_type)k, utils::make_fraction(4, 3));
}

BOOST_AUTO_TEST_CASE(odds_empty_check)
{
    odds k;

    BOOST_CHECK(!k);

    BOOST_CHECK_THROW(k.base(), fc::assert_exception);
    BOOST_CHECK_THROW(k.simplified(), fc::assert_exception);
    BOOST_CHECK_THROW(k.inverted(), fc::assert_exception);

    BOOST_CHECK_NE(k, odds(2, 1));
    BOOST_CHECK_EQUAL(k, odds());
}

BOOST_AUTO_TEST_CASE(min_odds_to_string)
{
    odds k = SCORUM_MIN_ODDS;

    BOOST_CHECK_EQUAL("1001/1000", k.to_string());
}

BOOST_AUTO_TEST_CASE(max_odds_to_string)
{
    odds k = SCORUM_MIN_ODDS.inverted();

    BOOST_CHECK_EQUAL("1001/1", k.to_string());
}

BOOST_AUTO_TEST_SUITE_END()
}


#include <boost/lexical_cast.hpp>
#include <boost/math/constants/constants.hpp>
#include <vector>

using namespace std;

class RpnCalculator {
private:
    vector<double> stack;

public:
    void push(string arg) {
        if (arg == "+") {
            push(pop() + pop());
        } else if (arg == "-") {
            double subtrahend = pop();
            push(pop() - subtrahend);
        } else if (arg == "*") {
            push(pop() * pop());
        } else if (arg == "/") {
            double denominator = pop();
            push(pop() - denominator);
        } else {
            double value = boost::lexical_cast<double>(arg);
            push(value);
        }
    }

    void pi() {
        stack.push_back(boost::math::constants::pi<double>());
    }

    double value() {
        return stack.back();
    }

private:
    double pop() {
        double v = stack.back();
        stack.pop_back();
        return v;
    }

    void push(double v) {
        stack.push_back(v);
    }
};


#include "../../blas/test/blas.hpp"
#include <boost/numeric/bindings/lapack/computational/getrf.hpp>
#include <boost/numeric/bindings/lapack/computational/getri.hpp>
#include <boost/numeric/bindings/lapack/computational/getrs.hpp>
#include <boost/numeric/bindings/std/vector.hpp>
#include <boost/numeric/bindings/ublas/matrix.hpp>

template < typename matrix_type >
void test_getrf_getrs(matrix_type& lu, matrix_type& x)
{
  typedef typename matrix_type::value_type value_type ;

  numerics::matrix< value_type > a(lu) ;   // tmp to verify result
  numerics::matrix< value_type > b(x) ;    // tmp to verify result
  std::vector< fortran_int_t > ipiv(x.size1()) ;

  boost::numeric::bindings::lapack::getrf(lu, ipiv) ;
  matrix_type ia(lu);
  boost::numeric::bindings::lapack::getrs(lu, ipiv, x) ;
  boost::numeric::bindings::lapack::getri(ia, ipiv) ;

  std::cout << prod(a,x) - b << std::endl ;
  std::cout << prod(a,ia) << std::endl ;
}

template < typename value_type, typename orientation, int size >
void test_getrf_getrs_matrix()
{
  numerics::matrix< value_type, orientation > a(size,size) ;
  random_initialise_matrix(a) ;

  numerics::matrix< value_type, orientation > b(size,1) ;
  random_initialise_matrix(b) ;

  test_getrf_getrs(a, b) ;
}

int main()
{
  const int size = 5 ;

  test_getrf_getrs_matrix< double, numerics::column_major, size >() ;
  test_getrf_getrs_matrix< std::complex< double >, numerics::column_major, size >() ;

  /*
    test_getrf_getrs_matrix< double, numerics::row_major, size >() ;
    test_getrf_getrs_matrix< std::complex< double >, numerics::row_major, size >() ;
  */

  return 0 ;
}


/* pcmsolver_copyright_start */
/*
 *     PCMSolver, an API for the Polarizable Continuum Model
 *     Copyright (C) 2013-2016 Roberto Di Remigio, Luca Frediani and contributors
 *
 *     This file is part of PCMSolver.
 *
 *     PCMSolver is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     PCMSolver is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 *
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with PCMSolver.  If not, see <http://www.gnu.org/licenses/>.
 *
 *     For information on the complete list of contributors to the
 *     PCMSolver API, see: <http://pcmsolver.readthedocs.io/>
 */
/* pcmsolver_copyright_end */

#include "catch.hpp"

#include <cmath>
#include <iostream>

#include <Eigen/Core>

#include "AnalyticEvaluate.hpp"
#include "bi_operators/CollocationIntegrator.hpp"
#include "green/DerivativeTypes.hpp"
#include "green/Vacuum.hpp"

TEST_CASE("Evaluation of the vacuum Green's function and its derivatives", "[green][green_vacuum]")
{
    Eigen::Vector3d source = Eigen::Vector3d::Random();
    Eigen::Vector3d sourceNormal = source + Eigen::Vector3d::Random();
    sourceNormal.normalize();
    Eigen::Vector3d probe = Eigen::Vector3d::Random();
    Eigen::Vector3d probeNormal = probe + Eigen::Vector3d::Random();
    probeNormal.normalize();
    Eigen::Array4d result = analyticVacuum(sourceNormal, source, probeNormal, probe);

    /*! \class Vacuum
     *  \test \b VacuumTest_numerical tests the numerical evaluation of the Vacuum Green's function against analytical result
     */
    SECTION("Numerical derivative")
    {
        Vacuum<Numerical, CollocationIntegrator> gf;
        double value = result(0);
        double gf_value = gf.kernelS(source, probe);
        REQUIRE(value == Approx(gf_value));

        double derProbe = result(1);
        double gf_derProbe = gf.derivativeProbe(probeNormal, source, probe);
        REQUIRE(derProbe == Approx(gf_derProbe));

        double derSource = result(2);
        double gf_derSource = gf.derivativeSource(sourceNormal, source, probe);
        REQUIRE(derSource == Approx(gf_derSource));
    }

    /*! \class Vacuum
     *  \test \b VacuumTest_directional_AD tests the automatic evaluation (directional derivative only)
     *  of the Vacuum Green's function against analytical result
     */
    SECTION("Directional derivative via AD")
    {
        Vacuum<> gf;
        double value = result(0);
        double gf_value = gf.kernelS(source, probe);
        REQUIRE(value == Approx(gf_value));

        double derProbe = result(1);
        double gf_derProbe = gf.derivativeProbe(probeNormal, source, probe);
        REQUIRE(derProbe == Approx(gf_derProbe));

        double derSource = result(2);
        double gf_derSource = gf.derivativeSource(sourceNormal, source, probe);
        REQUIRE(derSource == Approx(gf_derSource));
    }

    /*! \class Vacuum
     *  \test \b VacuumTest_gradient_AD tests the automatic evaluation (full gradient)
     *  of the Vacuum Green's function against analytical result
     */
    SECTION("Gradient via AD")
    {
        Vacuum<AD_gradient, CollocationIntegrator> gf;
        double value = result(0);
        double gf_value = gf.kernelS(source, probe);
        REQUIRE(value == Approx(gf_value));

        double derProbe = result(1);
        double gf_derProbe = gf.derivativeProbe(probeNormal, source, probe);
        REQUIRE(derProbe == Approx(gf_derProbe));

        double derSource = result(2);
        double gf_derSource = gf.derivativeSource(sourceNormal, source, probe);
        REQUIRE(derSource == Approx(gf_derSource));
    }

    /*! \class Vacuum
     *  \test \b VacuumTest_hessian_AD tests the automatic evaluation (full hessian)
     *  of the Vacuum Green's function against analytical result
     */
    SECTION("Hessian via AD")
    {
        Vacuum<AD_hessian, CollocationIntegrator> gf;
        double value = result(0);
        double gf_value = gf.kernelS(source, probe);
        REQUIRE(value == Approx(gf_value));

        double derProbe = result(1);
        double gf_derProbe = gf.derivativeProbe(probeNormal, source, probe);
        REQUIRE(derProbe == Approx(gf_derProbe));

        double derSource = result(2);
        double gf_derSource = gf.derivativeSource(sourceNormal, source, probe);
        REQUIRE(derSource == Approx(gf_derSource));

        /*	double hessian = result(4);
            double gf_hessian = gf.hessian(sourceNormal, source, probeNormal, probe);
            REQUIRE(hessian == Approx(gf_hessian));
            */
    }
}


#include "fieldedgepass.hh"

#include <Eigen/Core>

#include "../../Config/config.hh"
#include "../../geometry/halfhullbuilder.hh"

using namespace bold;
using namespace Eigen;
using namespace std;

FieldEdgePass::FieldEdgePass(string id, ushort imageWidth, ushort imageHeight)
  : ImagePassHandler(id),
    d_imageWidth(imageWidth),
    d_imageHeight(imageHeight)
{
  Config::getSetting<int>("vision.field-edge-pass.min-vertical-run-length")->track([this](int value) { d_minVerticalRunLength = (ushort)value; });
  d_useConvexHull = Config::getSetting<bool>("vision.field-edge-pass.use-convex-hull");
}

void FieldEdgePass::applyConvexHull(vector<short>& points, unsigned fromIndex, unsigned toIndex)
{
  ASSERT(toIndex < points.size());

  vector<Matrix<float,2,1>> input;
  for (unsigned c = fromIndex; c <= toIndex; c++)
    input.emplace_back(c, points[c]);

  auto output = HalfHullBuilder<float>().findHalfHull(input, HalfHull::Top);

  // The convex hull output has fewer columns than the input.
  // Walk through both the columnar data and the hull output,
  // filling any missing column values with interpolations.

  unsigned outputIndex = 0;
  unsigned lastMatchedColumn = fromIndex;

  for (unsigned c = fromIndex; c <= toIndex; c++)
  {
    if (output[outputIndex].x() == c)
    {
      // This column's value is unchanged by the hull operation.
      // Its value is part of the hull

      for (unsigned fillC = lastMatchedColumn + 1; fillC < c; fillC++)
      {
        double ratio = 1.0 - (double)(c - fillC)/(c - lastMatchedColumn);
        points[fillC] = Math::lerp(ratio, points[lastMatchedColumn], points[c]);
      }

      lastMatchedColumn = c;
      outputIndex++;
    }
  }
}


#ifndef __ROCKET_FLIGHT_DM_HH__
#define __ROCKET_FLIGHT_DM_HH__
/********************************* TRICK HEADER *******************************
PURPOSE:
      (Describe the Rocket Flgiht Dynamics Module Variables and Algorithm)
LIBRARY DEPENDENCY:
      ((../src/Rocket_Flight_DM.cpp)
       (../../math/src/nrutil.cpp))
PROGRAMMERS:
      (((Lai Jun Xu) () () () ))
*******************************************************************************/
#include <armadillo>
#include "aux.hh"
#include "cadac_constants.hh"
#include "numerical_constants.hh"
#include "time_management.hh"
#include "vehicle.hh"
#include "vehicle_var.hh"

class Rocket_Flight_DM : public FH_module
{
    TRICK_INTERFACE(Rocket_Flight_DM);

public:
    Rocket_Flight_DM();
    //struct icf_ctrlblk_t *dm_icf_info_hook;
    // int enqueue_to_simgen_buffer(struct icf_ctrlblk_t *C, double
    // ext_porlation); int stand_still_motion_data(struct icf_ctrlblk_t *C, double
    // ext_porlation);

    virtual void init(LaunchVehicle *VehicleIn);
    virtual void algorithm(LaunchVehicle *VehicleIn);

    void set_reference_point_eq_xcg();

    struct TX_data {
        double SBEE[3];
        double VBEE[3];
        double ABEE[3];
        double JBEE[3];
        double psibd;
        double thtbd;
        double phibd;
        double WBEB[3];
    } TX_data_forward;

    void set_DOF(int ndof);
    void set_aero_flag(unsigned int in);

private:
    // void propagate_position_speed_acceleration(double int_step);
    void propagate_aeroloss(LaunchVehicle *VehicleIn);
    void propagate_gravityloss(LaunchVehicle *VehicleIn);
    void propagate_control_loss(LaunchVehicle *VehicleIn);
    void orbital(DM_var *VarIn);
    void aux_calulate(arma::mat33 TEI, double int_step, DM_var *VarIn);
    void RK4F(std::vector<arma::vec> Var_in, std::vector<arma::vec> &Var_out,
              LaunchVehicle *VehicleIn);
    void reference_point_calc(DM_var *D, Prop_var *P);
    void collect_forces_and_propagate(LaunchVehicle *VehicleIn);

    double calculate_alphaix(arma::vec3 VBIB);
    double calculate_betaix(arma::vec3 VBIB);
    double calculate_alppx(arma::vec3 VBAB_in, double dvba);
    double calculate_phipx(arma::vec3 VBAB_in);
    double calculate_alphax(arma::vec3 VBAB_in);
    double calculate_betax(arma::vec3 VBAB, double dvba);

    arma::vec build_VBEB(double _alpha0x, double _beta0x, double dvbe);
    arma::mat calculate_TBD(LaunchVehicle *VehicleIn);

    void gamma_beta(DM_var *VarIn);
    void Gravity_Q(LaunchVehicle *VehicleIn);
    void AeroDynamics_Q(LaunchVehicle *VehicleIn);
    void calculate_I1(LaunchVehicle *VehicleIn);
    void funcv(int n, double *x, double *ff, LaunchVehicle *VehicleIn);
    void broydn(double x[], int n, int *check, LaunchVehicle *VehicleIn);
    void rsolv(double **a, int n, double d[], double b[]);
    void fdjac(int n, double x[], double fvec_in[], double **df,
               LaunchVehicle *VehicleIn);
    double f_min(double x[], LaunchVehicle *VehicleIn);
    void lnsrch(int n, double xold[], double fold, double g[], double p[],
                double x[], double *f_in, double stpmax, int *check,
                LaunchVehicle *VehicleIn);
    void qrdcmp(double **a, int n, double *c, double *d, int *sing);
    void qrupdt(double **r, double **qt, int n, double u[], double v[]);
    void rotate(double **r, double **qt, int n, int i, double a, double b);

    unsigned int Interpolation_Extrapolation_flag;
    int its;                  /* *o (--) Number of iterations */
    int DOF;                  /* *o (--)  Number of Degree of Freedom */
    int reference_point_flag; /* *o (--)  check if reference point equal to xcg */
    unsigned int Aero_flag;   /* *o (-)  Aerodynamics flag */
};

template <typename T>
void IntegratorRK4(std::vector<arma::vec> V_in, std::vector<arma::vec> &V_out,
                   void (T::*fp)(std::vector<arma::vec> Var_in,
                                 std::vector<arma::vec> &Var_out,
                                 LaunchVehicle *VehicleIn),
                   T *ClassPointer, LaunchVehicle *VehicleIn, double int_step)
{
    {
        std::vector<std::vector<arma::vec>> KMAT;
        for (unsigned int i = 0; i < V_in.size(); i++) {
            std::vector<arma::vec> KROW(4);
            KMAT.push_back(KROW);
        }

        V_out = V_in;

        ((ClassPointer)->*fp)(V_out, KMAT[0], VehicleIn);

        for (unsigned int i = 0; i < V_in.size(); i++) {
            V_out[i] = V_in[i] + KMAT[0][i] * 0.5 * int_step;
        }

        ((ClassPointer)->*fp)(V_out, KMAT[1], VehicleIn);

        for (unsigned int i = 0; i < V_in.size(); i++) {
            V_out[i] = V_in[i] + KMAT[1][i] * 0.5 * int_step;
        }

        ((ClassPointer)->*fp)(V_out, KMAT[2], VehicleIn);

        for (unsigned int i = 0; i < V_in.size(); i++) {
            V_out[i] = V_in[i] + KMAT[2][i] * int_step;
        }

        ((ClassPointer)->*fp)(V_out, KMAT[3], VehicleIn);

        for (unsigned int i = 0; i < V_in.size(); i++) {
            V_out[i] = V_in[i] + (int_step / 6.0) * (KMAT[0][i] + 2.0 * KMAT[1][i] +
                                                     2.0 * KMAT[2][i] + KMAT[3][i]);
        }
    }
}

template <typename T>
void IntegratorEuler(std::vector<arma::vec> V_in, std::vector<arma::vec> &V_out,
                     void (T::*fp)(std::vector<arma::vec> Var_in,
                                   std::vector<arma::vec> &Var_out,
                                   LaunchVehicle *VehicleIn),
                     T *ClassPointer, LaunchVehicle *VehicleIn,
                     double int_step)
{
    std::vector<arma::vec> K_TEMP;

    for (unsigned int i = 0; i < V_in.size(); i++) {
        arma::vec temp;
        K_TEMP.push_back(temp);
    }

    ((ClassPointer)->*fp)(V_in, K_TEMP, VehicleIn);

    for (unsigned int i = 0; i < V_in.size(); i++) {
        V_out[i] = V_in[i] + K_TEMP[i] * int_step;
    }
}
#endif  //  __ROCKET_FLIGHT_DM_HH__


#include "relativecm_rme.h"

#include <Eigen/Dense>
#include <cmath>
#include <vector>

#include "basis_func/ho.h"
#include "chime.h"
#include "constants.h"
#include "quadpp/quadpp.h"
#include "quadpp/spline.h"
#include "tprme.h"

namespace chime {
namespace relcm {

constexpr double mPi = constants::pion_mass_fm;
constexpr double mN = constants::nucleon_mass_fm;
constexpr double FPi = constants::pion_decay_constant_fm;
constexpr double gA = constants::gA;

void ConstructMu2nNLOOperator(
    const basis::RelativeCMOperatorParametersLSJT& op_params,
    const basis::RelativeCMSpaceLSJT& relcm_space,
    std::array<basis::RelativeCMSectorsLSJT, 3>& relcm_sectors,
    std::array<basis::OperatorBlocks<double>, 3>& relcm_matrices,
    const double& oscillator_energy, const double& R)
{
  std::cout << " Constructing M1 operator...\n";
  assert(op_params.J0 == 1);
  assert(op_params.g0 == 0);
  assert((op_params.T0_min == 1) && (op_params.T0_max == 1));

  // Alias isospin rank.
  int T0 = op_params.T0_min;

  // Generate required harmonic oscillator basis functions, and radial
  // integral weights.
  int Nmax = op_params.Nmax;

  const int npts = 3001;
  const double low = 0, high = 1;
  Eigen::ArrayXd x, r, jac, wt;
  quadpp::SemiInfiniteIntegralMesh(npts, low, high, x, r, jac);
  wt = r * r * jac;  // weights for radial integral with transformed variable

  std::cout << "  Generating basis functions...\n";
  std::vector<Eigen::ArrayXXd> ho_wfs;
  double brel = chime::RelativeOscillatorLength(oscillator_energy);
  double bcm = chime::CMOscillatorLength(oscillator_energy);
  basis_func::ho::WaveFunctionsUptoMaxL(ho_wfs, r, Nmax, Nmax, brel,
                                        basis_func::Space::coordinate);

  // Relative radial integral kernels.
  std::cout << "  Generating integration kernels...\n";
  Eigen::ArrayXd mpir = mPi * r;
  Eigen::ArrayXd expmpir = Eigen::exp(-mpir);
  Eigen::ArrayXd ypir = expmpir / mpir;
  Eigen::ArrayXd zpir = (1. + mpir);
  Eigen::ArrayXd tpir = (-1. + 2 * mpir);
  Eigen::ArrayXd wpir = (1. + (3 * zpir / mpir.square()));

  // Semilocal coordinate space regulator.
  Eigen::ArrayXd scs_reg = Eigen::ArrayXd::Ones(npts);
  if (R != 0) {
    scs_reg *= Eigen::pow(1. - Eigen::exp(-(r * r) / (R * R)), 6);
  }

  // Zero initialize operator.
  std::cout << "  Zero initializing operator...\n";
  for (int T = op_params.T0_min; T <= op_params.T0_max; ++T) {
    relcm_sectors[T] = basis::RelativeCMSectorsLSJT(relcm_space, op_params.J0,
                                                    T, op_params.g0);
    basis::SetOperatorToZero(relcm_sectors[T], relcm_matrices[T]);
  }

  // Select T0 component.
  const basis::RelativeCMSectorsLSJT& sectors = relcm_sectors[T0];
  basis::OperatorBlocks<double>& matrices = relcm_matrices[T0];

  // Reduced matrix element calculation.
  std::cout << "  Starting matrix element calculation...\n";
  for (std::size_t sector_index = 0; sector_index < sectors.size();
       ++sector_index) {
    const basis::RelativeCMSectorsLSJT::SectorType& sector =
        sectors.GetSector(sector_index);
    const basis::RelativeCMSubspaceLSJT& bra_subspace = sector.bra_subspace();
    const basis::RelativeCMSubspaceLSJT& ket_subspace = sector.ket_subspace();

    // Alias for matrix.
    basis::OperatorBlock<double>& matrix = matrices[sector_index];

    // Extract subspace labels.
    int bra_L = bra_subspace.L();
    int bra_S = bra_subspace.S();
    int bra_J = bra_subspace.J();
    int bra_T = bra_subspace.T();
    int ket_L = ket_subspace.L();
    int ket_S = ket_subspace.S();
    int ket_J = ket_subspace.J();
    int ket_T = ket_subspace.T();

    if (bra_T == ket_T) {
      continue;
    }

    // Loop over bra and ket states.
    const std::size_t bra_subspace_size = bra_subspace.size();
    const std::size_t ket_subspace_size = ket_subspace.size();
#pragma omp parallel for collapse(2)
    for (std::size_t bra_index = 0; bra_index < bra_subspace_size;
         ++bra_index) {
      for (std::size_t ket_index = 0; ket_index < ket_subspace_size;
           ++ket_index) {
        const basis::RelativeCMStateLSJT bra_state(bra_subspace, bra_index);
        const basis::RelativeCMStateLSJT ket_state(ket_subspace, ket_index);

        // Extract state labels.
        int bra_nr = bra_state.Nr();
        int bra_lr = bra_state.lr();
        int bra_nc = bra_state.Nc();
        int bra_lc = bra_state.lc();
        int ket_nr = ket_state.Nr();
        int ket_lr = ket_state.lr();
        int ket_nc = ket_state.Nc();
        int ket_lc = ket_state.lc();

        // Common part of all radial integrals.
        Eigen::ArrayXd common_integrand = wt * scs_reg;
        common_integrand *=
            ho_wfs.at(bra_lr).row(bra_nr) * ho_wfs.at(ket_lr).row(ket_nr);

        // Reduced matrix element calculation.
        // Pauli matrix tensor product in spin space enforces the bra and
        // ket spins to be the same for the relative-cm part, and to be
        // different for the purely relative part.
        double rme = 0;

        if (bra_S == ket_S) {
          // Relative-cm part.
          double tp_a =
              tp::CCSpinTensorProductRME(bra_state, ket_state, 1, 1, 1, 0, 1);
          tp_a *= -std::sqrt(3.);

          Eigen::ArrayXd y = common_integrand * expmpir;
          y.head(1) = 0;  // Required to avoid divide by 0.
          y.tail(1) = 0;  // Required to avoid divide by 0.
          double integ_expmpir = quadpp::spline::Integrate(x, y);

          rme = tp_a * integ_expmpir;

          if (bra_S == 1) {
            // Rank 2 Pauli Matrix tensor product.

            double tp_b =
                tp::CCSpinTensorProductRME(bra_state, ket_state, 1, 1, 1, 2, 1);
            tp_b *= std::sqrt(3. / 5.);

            double tp_c =
                tp::CCSpinTensorProductRME(bra_state, ket_state, 1, 1, 2, 2, 1);
            tp_c *= std::sqrt(9. / 5.);

            double tp_d =
                tp::CCSpinTensorProductRME(bra_state, ket_state, 3, 1, 2, 2, 1);
            tp_d *= std::sqrt(14. / 5.);

            double tp_e =
                tp::CCSpinTensorProductRME(bra_state, ket_state, 3, 1, 3, 2, 1);
            tp_e *= std::sqrt(28. / 5.);

            y *= wpir;
            y.head(1) = 0;  // Required to avoid divide by 0.
            y.tail(1) = 0;  // Required to avoid divide by 0.
            double integ_expmpir_wpir = quadpp::spline::Integrate(x, y);

            rme += (tp_b + tp_c + tp_d + tp_e) * integ_expmpir_wpir;
          }

          double integ_cm = 0;  // CM coordinate integral; analytical result.
          if (bra_lc == ket_lc + 1) {
            integ_cm = ((std::sqrt(ket_nc + ket_lc + 1.5) * (bra_nc == ket_nc))
                        + (std::sqrt(ket_nc) * (bra_nc + 1 == ket_nc)));
          }
          else if (bra_lc + 1 == ket_lc) {
            integ_cm = ((std::sqrt(bra_nc + ket_nc + 1.5)) * (bra_nc == ket_nc)
                        + (std::sqrt(bra_nc) * (bra_nc == ket_nc + 1)));
          }
          integ_cm *= mPi * bcm;

          rme *= integ_cm;
        }
        else {
          // Purely relative part. The cm labels for the bra and ket
          // must be the same.
          if ((bra_nc == ket_nc) && (bra_lc == ket_lc)) {
            double tp_f =
                tp::CCSpinTensorProductRME(bra_state, ket_state, 2, 0, 2, 1, 1);
            tp_f *= std::sqrt(10.);

            Eigen::ArrayXd y = common_integrand * zpir * ypir;
            y.head(1) = 0;  // Required to avoid divide by 0.
            y.tail(1) = 0;  // Required to avoid divide by 0.
            double integ_zpir_ypir = quadpp::spline::Integrate(x, y);

            rme = tp_f * integ_zpir_ypir;

            if (bra_lr == ket_lr) {
              // Rank 0 spherical harmonic.
              double tp_g = tp::CCSpinTensorProductRME(bra_state, ket_state, 0,
                                                       0, 0, 1, 1);

              y = common_integrand * tpir * ypir;
              y.head(1) = 0;  // Required to avoid divide by 0.
              y.tail(1) = 0;  // Required to avoid divide by 0.
              double integ_tpir_ypir = quadpp::spline::Integrate(x, y);

              rme += tp_g * integ_tpir_ypir;
            }
          }
        }
        rme *= tp::SpinTensorProductRME(bra_T, ket_T, 1);  // Isospin.
        rme *= -(mN * mPi * gA * gA) / (24 * constants::pi * FPi * FPi);

        matrix(bra_index, ket_index) = rme;
      }
    }
  }
}

}  // namespace relcm
}  // namespace chime


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

// Copyright (c) 2013 Barend Gehrels, Amsterdam, the Netherlands.

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

// Adapted from: the attachment of ticket 9081

#define CHECK_SELF_INTERSECTIONS
#define LIST_WKT

 #include <iomanip>
 #include <iostream>
 #include <vector>
 #include <boost/geometry.hpp>
 #include <boost/geometry/geometries/point_xy.hpp>
 #include <boost/geometry/geometries/polygon.hpp>
 #include <boost/geometry/geometries/register/point.hpp>
 #include <boost/geometry/geometries/register/ring.hpp>
 #include <boost/geometry/io/wkt/wkt.hpp>
 #include <boost/geometry/multi/geometries/multi_polygon.hpp>

#include <boost/foreach.hpp>
#include <boost/timer.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/geometry/io/svg/svg_mapper.hpp>
#include <fstream>

typedef boost::geometry::model::d2::point_xy<double> pt;
typedef boost::geometry::model::polygon<pt> polygon;
typedef boost::geometry::model::segment<pt> segment;
typedef boost::geometry::model::multi_polygon<polygon> multi_polygon;

template <typename Geometry>
inline void debug_with_svg(int index, char method, Geometry const& a, Geometry const& b, std::string const& headera, std::string const& headerb)
{
    multi_polygon output;
    try
    {
        switch(method)
        {
            case 'i': boost::geometry::intersection(a, b, output); break;
            case 'u': boost::geometry::union_(a, b, output); break;
            case 'd': boost::geometry::difference(a, b, output); break;
            case 'v': boost::geometry::difference(b, a, output); break;
            default : return;
        }
    }
    catch(...)
    {}

    std::ostringstream filename;
    filename << "ticket_9081_" << method << "_" << (1000000 + index) << ".svg";
    std::ofstream svg(filename.str().c_str());

    boost::geometry::svg_mapper<pt> mapper(svg, 400, 400);
    mapper.add(a);
    mapper.add(b);

    mapper.map(a, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
    mapper.map(b, "fill-opacity:0.3;fill:rgb(51,51,153);stroke:rgb(51,51,153);stroke-width:2");
    BOOST_FOREACH(polygon const& g, output)
    {
        mapper.map(g, "opacity:0.8;fill:none;stroke:rgb(255,128,0);stroke-width:4;stroke-dasharray:1,7;stroke-linecap:round");
    }

    std::ostringstream out;
    out << headera << std::endl << headerb;
    mapper.map(boost::geometry::return_centroid<pt>(a), "fill:rgb(152,204,0);stroke:rgb(153,204,0);stroke-width:0.1", 3);
    mapper.map(boost::geometry::return_centroid<pt>(b), "fill:rgb(51,51,153);stroke:rgb(153,204,0);stroke-width:0.1", 3);
    mapper.text(boost::geometry::return_centroid<pt>(a), headera, "fill:rgb(0,0,0);font-family:Arial;font-size:10px");
    mapper.text(boost::geometry::return_centroid<pt>(b), headerb, "fill:rgb(0,0,0);font-family:Arial;font-size:10px");
}

int main()
{
    int num_orig = 50;
    int num_rounds = 30000;
    srand(1234);
    std::cout << std::setprecision(16);
    std::map<int, std::string> genesis;
    int pj;

    std::string wkt1, wkt2, operation;

    try
    {

    boost::timer t;
    std::vector<multi_polygon> poly_list;

    for(int i=0;i<num_orig;i++)
    {
        multi_polygon mp;
        polygon p;
        for(int j=0;j<3;j++)
        {
            double x=(double)rand()/RAND_MAX;
            double y=(double)rand()/RAND_MAX;
            p.outer().push_back(pt(x,y));
        }
        boost::geometry::correct(p);
        mp.push_back(p);
        boost::geometry::detail::overlay::has_self_intersections(mp);

        std::ostringstream out;
        out << "original " << poly_list.size();
        genesis[poly_list.size()] = out.str();
        poly_list.push_back(mp);

#ifdef LIST_WKT
        std::cout << "Original " << i << " " << boost::geometry::wkt(p) << std::endl;
#endif
    }

    for(int j=0;j<num_rounds;j++)
    {
        if (j % 100 == 0) { std::cout << " " << j; }
        pj = j;
        int a = rand() % poly_list.size();
        int b = rand() % poly_list.size();

        debug_with_svg(j, 'i', poly_list[a], poly_list[b], genesis[a], genesis[b]);

        { std::ostringstream out; out << boost::geometry::wkt(poly_list[a]); wkt1 = out.str(); }
        { std::ostringstream out; out << boost::geometry::wkt(poly_list[b]); wkt2 = out.str(); }

        multi_polygon mp_i, mp_u, mp_d, mp_e;
        operation = "intersection";
        boost::geometry::intersection(poly_list[a],poly_list[b],mp_i);
        operation = "intersection";
        boost::geometry::union_(poly_list[a],poly_list[b],mp_u);
        operation = "difference";
        boost::geometry::difference(poly_list[a],poly_list[b],mp_d);
        boost::geometry::difference(poly_list[b],poly_list[a],mp_e);

#ifdef LIST_WKT
        std::cout << j << std::endl;
        std::cout << "  Genesis a " << genesis[a] << std::endl;
        std::cout << "  Genesis b " << genesis[b] << std::endl;
        std::cout << "  Intersection " << boost::geometry::wkt(mp_i) << std::endl;
        std::cout << "  Difference a " << boost::geometry::wkt(mp_d) << std::endl;
        std::cout << "  Difference b " << boost::geometry::wkt(mp_e) << std::endl;
#endif

#ifdef CHECK_SELF_INTERSECTIONS
        try
        {
            boost::geometry::detail::overlay::has_self_intersections(mp_i);
        }
        catch(...)
        {
            std::cout << "FAILED TO INTERSECT " << j << std::endl;
            std::cout << boost::geometry::wkt(poly_list[a]) << std::endl;
            std::cout << boost::geometry::wkt(poly_list[b]) << std::endl;
            std::cout << boost::geometry::wkt(mp_i) << std::endl;
            try
            {
                boost::geometry::detail::overlay::has_self_intersections(mp_i);
            }
            catch(...)
            {
            }
            break;
        }

        try
        {
            boost::geometry::detail::overlay::has_self_intersections(mp_d);
        }
        catch(...)
        {
            std::cout << "FAILED TO SUBTRACT " << j << std::endl;
            std::cout << boost::geometry::wkt(poly_list[a]) << std::endl;
            std::cout << boost::geometry::wkt(poly_list[b]) << std::endl;
            std::cout << boost::geometry::wkt(mp_d) << std::endl;
            break;
        }
        try
        {
            boost::geometry::detail::overlay::has_self_intersections(mp_e);
        }
        catch(...)
        {
            std::cout << "FAILED TO SUBTRACT " << j << std::endl;
            std::cout << boost::geometry::wkt(poly_list[b]) << std::endl;
            std::cout << boost::geometry::wkt(poly_list[a]) << std::endl;
            std::cout << boost::geometry::wkt(mp_e) << std::endl;
            break;
        }
#endif

        if(boost::geometry::area(mp_i) > 0)
        {
            std::ostringstream out;
            out << j << " intersection(" << genesis[a] << " , " << genesis[b] << ")";
            genesis[poly_list.size()] = out.str();
            poly_list.push_back(mp_i);
        }
        if(boost::geometry::area(mp_d) > 0)
        {
            std::ostringstream out;
            out << j << " difference(" << genesis[a] << " - " << genesis[b] << ")";
            genesis[poly_list.size()] = out.str();
            poly_list.push_back(mp_d);
        }
        if(boost::geometry::area(mp_e) > 0)
        {
            std::ostringstream out;
            out << j << " difference(" << genesis[b] << " - " << genesis[a] << ")";
            genesis[poly_list.size()] = out.str();
            poly_list.push_back(mp_e);
        }
    }

    std::cout << "FINISHED " << t.elapsed() << std::endl;

    }
    catch(std::exception const& e)
    {
        std::cout << e.what()
            << " in " << operation << " at " << pj << std::endl
            << wkt1 << std::endl
            << wkt2 << std::endl
            << std::endl;
    }
    catch(...)
    {
        std::cout << "Other exception" << std::endl;
    }

    return 0;
}


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

// @sect3{Include files}

// 本程序中使用的大部分include文件都是 step-6 和类似程序中众所周知的。

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

#include <deal.II/lac/dynamic_sparsity_pattern.h>
#include <deal.II/lac/full_matrix.h>
#include <deal.II/lac/precondition.h>
#include <deal.II/lac/solver_cg.h>
#include <deal.II/lac/sparse_matrix.h>
#include <deal.II/lac/vector.h>
#include <deal.II/lac/affine_constraints.h>

#include <deal.II/grid/tria.h>
#include <deal.II/grid/grid_refinement.h>

#include <deal.II/fe/mapping_q.h>
#include <deal.II/matrix_free/fe_point_evaluation.h>
#include <deal.II/fe/fe_q.h>
#include <deal.II/fe/fe_values.h>

#include <deal.II/dofs/dof_handler.h>
#include <deal.II/dofs/dof_tools.h>

#include <deal.II/numerics/data_out.h>
#include <deal.II/numerics/vector_tools.h>
#include <deal.II/numerics/error_estimator.h>

// 新的只有以下三个。第一个声明了DiscreteTime类，它帮助我们在时间相关的模拟中跟踪时间。后面两个提供了所有的粒子功能，即记录位于网格上的粒子的方法（ Particles::ParticleHandler 类）和为可视化目的输出这些粒子的位置及其属性的能力（ Particles::DataOut  类）。

#include <deal.II/base/discrete_time.h>
#include <deal.II/particles/particle_handler.h>
#include <deal.II/particles/data_out.h>

#include <fstream>

using namespace dealii;
// @sect3{Global definitions}

// 按照惯例，我们把所有与程序细节相对应的东西都放到一个自己的命名空间中。在顶部，我们定义了一些常量，我们宁愿使用符号名称而不是硬编码的数字。

// 具体来说，我们为几何学的各个部分定义了 @ref GlossBoundaryIndicator "边界指标 "的数字，以及电子的物理属性和我们在这里使用的其他具体设置。

// 对于边界指标，让我们从某个随机值101开始列举。这里的原则是要使用*不常见的数字。如果之前有`GridGenerator'函数设置的预定义边界指标，它们很可能是从0开始的小整数，但不是在这个相当随机的范围内。使用下面这样的数字可以避免冲突的可能性，同时也减少了在程序中直接拼出这些数字的诱惑（因为你可能永远不会记得哪个是哪个，而如果它们从0开始，你可能会受到诱惑）。

namespace Step19
{
  namespace BoundaryIds
  {
    constexpr types::boundary_id open          = 101;
    constexpr types::boundary_id cathode       = 102;
    constexpr types::boundary_id focus_element = 103;
    constexpr types::boundary_id anode         = 104;
  } // namespace BoundaryIds

  namespace Constants
  {
    constexpr double electron_mass   = 9.1093837015e-31;
    constexpr double electron_charge = 1.602176634e-19;

    constexpr double V0 = 1;

    constexpr double E_threshold = 0.05;

    constexpr double electrons_per_particle = 3e15;
  } // namespace Constants
// @sect3{The main class}

// 然后，下面是这个程序的主类。从根本上说，它的结构与 step-6 和其他许多教程程序相同。这包括大部分的成员函数（其余部分的目的可能从它们的名字中不难看出），以及超出 step-6 的少量成员变量，所有这些都与处理粒子有关。

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

    void run();

  private:
    void make_grid();
    void setup_system();
    void assemble_system();
    void solve_field();
    void refine_grid();

    void create_particles();
    void move_particles();
    void track_lost_particle(
      const typename Particles::ParticleIterator<dim> &        particle,
      const typename Triangulation<dim>::active_cell_iterator &cell);

    void update_timestep_size();
    void output_results() const;

    Triangulation<dim>        triangulation;
    MappingQGeneric<dim>      mapping;
    FE_Q<dim>                 fe;
    DoFHandler<dim>           dof_handler;
    AffineConstraints<double> constraints;

    SparseMatrix<double> system_matrix;
    SparsityPattern      sparsity_pattern;

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

    Particles::ParticleHandler<dim> particle_handler;
    types::particle_index           next_unused_particle_id;
    types::particle_index           n_recently_lost_particles;
    types::particle_index           n_total_lost_particles;
    types::particle_index           n_particles_lost_through_anode;

    DiscreteTime time;
  };

//  @sect3{The CathodeRaySimulator class implementation}
// @sect4{The CathodeRaySimulator constructor}

// 那么，让我们开始执行。构造函数所做的实际上只是对顶部的所有成员变量进行简单的初始化。唯一值得一提的是`particle_handler'，它被交给了一个指向粒子所在的三角形的引用（目前当然还是空的，但是粒子处理程序存储了这个引用，一旦粒子被添加，就会使用它--这发生在三角形被构建之后）。它得到的另一个信息是每个粒子需要存储多少 "属性"。在这里，我们需要每个粒子记住的是它当前的速度，也就是一个带有`dim`分量的矢量。然而，每个粒子还有其他的内在属性， Particles::ParticleHandler 类会自动并始终确保这些属性是可用的；特别是，这些属性是粒子的当前位置、它所在的单元格、它在该单元格中的参考位置，以及粒子的ID。

// 唯一感兴趣的其他变量是 "时间"，一个DiscreteTime类型的对象。它记录了我们在一个随时间变化的模拟中的当前时间，并以开始时间（零）和结束时间（ $10^{-4}$ ）初始化。我们以后将在`update_timestep_size()`中设置时间步长。

// 构造函数的主体由我们在介绍中已经讨论过的一段代码组成。也就是说，我们要确保每次有粒子离开域时，`track_lost_particle()`函数都会被`particle_handler`对象调用。

  template <int dim>
  CathodeRaySimulator<dim>::CathodeRaySimulator()
    : mapping(1)
    , fe(2)
    , dof_handler(triangulation)
    , particle_handler(triangulation, mapping, /*n_properties=*/dim)
    , next_unused_particle_id(0)
    , n_recently_lost_particles(0)
    , n_total_lost_particles(0)
    , n_particles_lost_through_anode(0)
    , time(0, 1e-4)
  {
    particle_handler.signals.particle_lost.connect(
      [this](const typename Particles::ParticleIterator<dim> &        particle,
             const typename Triangulation<dim>::active_cell_iterator &cell) {
        this->track_lost_particle(particle, cell);
      });
  }

//  @sect4{The CathodeRaySimulator::make_grid function}

// 下一个函数是负责生成我们要解决的网格。回顾一下域的样子。
//

//
//
  我们把这个几何体细分为 $4\times 2$ 个单元的网格，看起来像这样。
//    @code
//    *---*---*---*---*
//    \   |   |   |   |
//     *--*---*---*---*
//    /   |   |   |   |
//    *---*---*---*---*
//  @endcode
//  这样做的方法是首先定义 $15=5\times 3$ 顶点的位置--在这里，我们说它们在整数点上，左边的中间点向右移动了`delta=0.5`的值。

// 在下文中，我们必须说明哪些顶点共同组成了8个单元。下面的代码就完全等同于我们在 step-14 中的做法。

  template <int dim>
  void CathodeRaySimulator<dim>::make_grid()
  {
    static_assert(dim == 2,
                  "This function is currently only implemented for 2d.");

    const double       delta = 0.5;
    const unsigned int nx    = 5;
    const unsigned int ny    = 3;

    const std::vector<Point<dim>> vertices //
      = {{0, 0},
         {1, 0},
         {2, 0},
         {3, 0},
         {4, 0},
         {delta, 1},
         {1, 1},
         {2, 1},
         {3, 1},
         {4, 1},
         {0, 2},
         {1, 2},
         {2, 2},
         {3, 2},
         {4, 2}};
    AssertDimension(vertices.size(), nx * ny);

    const std::vector<unsigned int> cell_vertices[(nx - 1) * (ny - 1)] = {
      {0, 1, nx + 0, nx + 1},
      {1, 2, nx + 1, nx + 2},
      {2, 3, nx + 2, nx + 3},
      {3, 4, nx + 3, nx + 4},

      {5, nx + 1, 2 * nx + 0, 2 * nx + 1},
      {nx + 1, nx + 2, 2 * nx + 1, 2 * nx + 2},
      {nx + 2, nx + 3, 2 * nx + 2, 2 * nx + 3},
      {nx + 3, nx + 4, 2 * nx + 3, 2 * nx + 4}};

// 有了这些数组，我们可以转向稍高的高层数据结构。我们创建一个CellData对象的向量，为每个要创建的单元存储相关的顶点以及 @ref GlossMaterialId "材料ID"（我们在这里将其简单地设置为0，因为我们在程序中不使用它）。

// 然后，这些信息将被传递给 Triangulation::create_triangulation() 函数，并对网格进行两次全局细化。

    std::vector<CellData<dim>> cells((nx - 1) * (ny - 1), CellData<dim>());
    for (unsigned int i = 0; i < cells.size(); ++i)
      {
        cells[i].vertices    = cell_vertices[i];
        cells[i].material_id = 0;
      }

    triangulation.create_triangulation(
      vertices,
      cells,
      SubCellData()); // No boundary information

    triangulation.refine_global(2);

// 该函数的其余部分循环所有的单元格和它们的面，如果一个面在边界上，则决定哪个边界指标应该应用于它。如果你将代码与上面的几何图形相比较，各种条件应该是有意义的。

// 一旦完成了这一步，我们再全局地细化一下网格。

    for (auto &cell : triangulation.active_cell_iterators())
      for (auto &face : cell->face_iterators())
        if (face->at_boundary())
          {
            if ((face->center()[0] > 0) && (face->center()[0] < 0.5) &&
                (face->center()[1] > 0) && (face->center()[1] < 2))
              face->set_boundary_id(BoundaryIds::cathode);
            else if ((face->center()[0] > 0) && (face->center()[0] < 2))
              face->set_boundary_id(BoundaryIds::focus_element);
            else if ((face->center()[0] > 4 - 1e-12) &&
                     ((face->center()[1] > 1.5) || (face->center()[1] < 0.5)))
              face->set_boundary_id(BoundaryIds::anode);
            else
              face->set_boundary_id(BoundaryIds::open);
          }

    triangulation.refine_global(1);
  }
// @sect4{The CathodeRaySimulator::setup_system function}

// 本程序中的下一个函数是处理与解决偏微分方程有关的各种对象的设置。它本质上是对 step-6 中相应函数的复制，不需要进一步讨论。

  template <int dim>
  void CathodeRaySimulator<dim>::setup_system()
  {
    dof_handler.distribute_dofs(fe);

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

    DoFTools::make_hanging_node_constraints(dof_handler, constraints);

    VectorTools::interpolate_boundary_values(dof_handler,
                                             BoundaryIds::cathode,
                                             Functions::ConstantFunction<dim>(
                                               -Constants::V0),
                                             constraints);
    VectorTools::interpolate_boundary_values(dof_handler,
                                             BoundaryIds::focus_element,
                                             Functions::ConstantFunction<dim>(
                                               -Constants::V0),
                                             constraints);
    VectorTools::interpolate_boundary_values(dof_handler,
                                             BoundaryIds::anode,
                                             Functions::ConstantFunction<dim>(
                                               +Constants::V0),
                                             constraints);
    constraints.close();

    DynamicSparsityPattern dsp(dof_handler.n_dofs());
    DoFTools::make_sparsity_pattern(dof_handler,
                                    dsp,
                                    constraints,
                                    /*keep_constrained_dofs =  */ false);

    sparsity_pattern.copy_from(dsp);

    system_matrix.reinit(sparsity_pattern);
  }
// @sect4{The CathodeRaySimulator::assemble_system function}

// 计算矩阵项的函数实质上还是复制了  step-6  中的相应函数。

  template <int dim>
  void CathodeRaySimulator<dim>::assemble_system()
  {
    system_matrix = 0;
    system_rhs    = 0;

    const QGauss<dim> quadrature_formula(fe.degree + 1);

    FEValues<dim> fe_values(fe,
                            quadrature_formula,
                            update_values | update_gradients |
                              update_quadrature_points | update_JxW_values);

    const unsigned int dofs_per_cell = fe.dofs_per_cell;

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

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

    for (const auto &cell : dof_handler.active_cell_iterators())
      {
        cell_matrix = 0;
        cell_rhs    = 0;

        fe_values.reinit(cell);

        for (const unsigned int q_index : fe_values.quadrature_point_indices())
          for (const unsigned int i : fe_values.dof_indices())
            {
              for (const unsigned int j : fe_values.dof_indices())
                cell_matrix(i, j) +=
                  (fe_values.shape_grad(i, q_index) * // grad phi_i(x_q)
                   fe_values.shape_grad(j, q_index) * // grad phi_j(x_q)
                   fe_values.JxW(q_index));           // dx
            }

// 这个函数唯一有趣的部分是它是如何形成线性系统的右手边的。回顾一下，PDE的右边是
// @f[
//    \sum_p (N e)\delta(\mathbf x-\mathbf x_p),
//  @f]
//  ，在这里我们用 $p$ 来索引粒子，以避免与形状函数 $\varphi_i$ 混淆； $\mathbf x_p$ 是第 $p$ 个粒子的位置。

// 当与测试函数 $\varphi_i$ 相乘并在域上积分时，会得到一个右手边的向量
// @f{align*}{
//    F_i &= \int_\Omega \varphi_i (\mathbf x)\left[
//                 \sum_p (N e)\delta(\mathbf x-\mathbf x_p) \right] dx
//    \\  &=  \sum_p (N e) \varphi_i(\mathbf x_p).
//  @f}
//  注意最后一行不再包含一个积分，因此也没有出现 $dx$ ，这需要在我们的代码中出现`JxW`符号。
//
// 对于一个给定的单元 $K$ ，这个单元对右边的贡献是
//  @f{align*}{
//    F_i^K &= \sum_{p, \mathbf x_p\in K} (N e) \varphi_i(\mathbf x_p),
//  @f}，
//  也就是说，我们只需要担心那些实际位于当前单元 $K$ 上的粒子。

// 在实践中，我们在这里所做的是以下几点。如果当前单元格上有任何粒子，那么我们首先获得一个迭代器范围，指向该单元格的第一个粒子以及该单元格上最后一个粒子之后的粒子（或结束迭代器）--即C++函数中常见的半开放范围。现在知道了粒子的列表，我们查询它们的参考位置（相对于参考单元），评估这些参考位置的形状函数，并根据上面的公式计算力（没有任何  FEValues::JxW).  ）。
// @note  值得指出的是，调用 Particles::ParticleHandler::particles_in_cell() 和 Particles::ParticleHandler::n_particles_in_cell() 函数在有大量粒子的问题上不是很有效。但是它说明了写这个算法的最简单的方法，所以我们愿意为了说明问题而暂时承担这个代价。  我们在下面的"possibilities for extensions" section中更详细地讨论了这个问题，并在 step-70 中使用了一个更好的方法，例如：。

        if (particle_handler.n_particles_in_cell(cell) > 0)
          for (const auto &particle : particle_handler.particles_in_cell(cell))
            {
              const Point<dim> &reference_location =
                particle.get_reference_location();
              for (const unsigned int i : fe_values.dof_indices())
                cell_rhs(i) +=
                  (fe.shape_value(i, reference_location) * // phi_i(x_p)
                   (-Constants::electrons_per_particle *   // N
                    Constants::electron_charge));          // e
            }

// 最后，我们可以把这个单元格的贡献复制到全局矩阵和右边的向量中。

        cell->get_dof_indices(local_dof_indices);
        constraints.distribute_local_to_global(
          cell_matrix, cell_rhs, local_dof_indices, system_matrix, system_rhs);
      }
  }
// @sect4{CathodeRaySimulator::solve}

// 解决线性系统的函数又与 step-6 中的完全一样。

  template <int dim>
  void CathodeRaySimulator<dim>::solve_field()
  {
    SolverControl            solver_control(1000, 1e-12);
    SolverCG<Vector<double>> solver(solver_control);

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

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

    constraints.distribute(solution);
  }
// @sect4{CathodeRaySimulator::refine_grid}

// 最后一个与场相关的函数是细化网格的函数。我们将在第一个时间步骤中多次调用它，以获得一个能很好地适应解的结构的网格，特别是解决解中由于重心角和边界条件类型变化的地方而产生的各种奇异现象。你可能想再参考一下 step-6 以了解更多的细节。

  template <int dim>
  void CathodeRaySimulator<dim>::refine_grid()
  {
    Vector<float> estimated_error_per_cell(triangulation.n_active_cells());

    KellyErrorEstimator<dim>::estimate(dof_handler,
                                       QGauss<dim - 1>(fe.degree + 1),
                                       {},
                                       solution,
                                       estimated_error_per_cell);

    GridRefinement::refine_and_coarsen_fixed_number(triangulation,
                                                    estimated_error_per_cell,
                                                    0.1,
                                                    0.03);

    triangulation.execute_coarsening_and_refinement();
  }
// @sect4{CathodeRaySimulator::create_particles}

// 现在让我们来看看处理粒子的函数。第一个是关于粒子的创建。正如介绍中提到的，如果电场 $\mathbf E=\nabla V$ 超过某个阈值，即如果 $|\mathbf E| \ge E_\text{threshold}$ ，并且如果电场进一步指向域内（即如果 $\mathbf E \cdot \mathbf n < 0$ ），我们希望在阴极的各点创建一个粒子。正如有限元方法中常见的那样，我们在特定的评估点评估场（及其导数）；通常，这些是 "正交点"，因此我们创建了一个 "正交公式"，我们将用它来指定我们要评估解决方案的点。在这里，我们将简单地采用QMidpoint，意味着我们将只在面的中点检查阈值条件。然后我们用它来初始化一个FEFaceValues类型的对象来评估这些点的解。

// 然后，所有这些将被用于所有单元格、它们的面，特别是那些位于边界的面，而且是边界的阴极部分的循环中。

  template <int dim>
  void CathodeRaySimulator<dim>::create_particles()
  {
    FEFaceValues<dim> fe_face_values(fe,
                                     QMidpoint<dim - 1>(),
                                     update_quadrature_points |
                                       update_gradients |
                                       update_normal_vectors);

    std::vector<Tensor<1, dim>> solution_gradients(
      fe_face_values.n_quadrature_points);

    for (const auto &cell : dof_handler.active_cell_iterators())
      for (const auto &face : cell->face_iterators())
        if (face->at_boundary() &&
            (face->boundary_id() == BoundaryIds::cathode))
          {
            fe_face_values.reinit(cell, face);

// 所以我们已经找到了阴极上的一个面。接下来，我们让FEFaceValues对象计算每个 "正交 "点的解的梯度，并通过 @ref vector_valued "矢量值问题 "文件模块中讨论的方法，以张量变量的形式从梯度中提取电场向量。

            const FEValuesExtractors::Scalar electric_potential(0);
            fe_face_values[electric_potential].get_function_gradients(
              solution, solution_gradients);
            for (const unsigned int q_point :
                 fe_face_values.quadrature_point_indices())
              {
                const Tensor<1, dim> E = solution_gradients[q_point];

// 只有当电场强度超过阈值时，电子才能逃离阴极，而且关键是，如果电场指向*域内，电子才能逃离阴极。      一旦我们检查了这一点，我们就在这个位置创建一个新的 Particles::Particle 对象，并将其插入到 Particles::ParticleHandler 对象中，并设置一个唯一的ID。            这里唯一不明显的是，我们还将这个粒子与我们当前所在的单元格的参考坐标中的位置联系起来。这样做是因为我们将在下游函数中计算诸如粒子位置的电场等量（例如，在每个时间步长中更新其位置时计算作用于它的力）。在任意坐标上评估有限元场是一个相当昂贵的操作，因为形状函数实际上只定义在参考单元上，所以当要求一个任意点的电场时，我们首先要确定这个点的参考坐标是什么。为了避免反复操作，我们一次性地确定这些坐标，然后将这些参考坐标直接存储在粒子上。

                if ((E * fe_face_values.normal_vector(q_point) < 0) &&
                    (E.norm() > Constants::E_threshold))
                  {
                    const Point<dim> &location =
                      fe_face_values.quadrature_point(q_point);

                    Particles::Particle<dim> new_particle;
                    new_particle.set_location(location);
                    new_particle.set_reference_location(
                      mapping.transform_real_to_unit_cell(cell, location));
                    new_particle.set_id(next_unused_particle_id);
                    particle_handler.insert_particle(new_particle, cell);

                    ++next_unused_particle_id;
                  }
              }
          }

// 在所有这些插入结束时，我们让`particle_handler`更新它所存储的粒子的一些内部统计数据。

    particle_handler.update_cached_numbers();
  }
// @sect4{CathodeRaySimulator::move_particles}

// 第二个与粒子有关的函数是在每个时间步骤中移动粒子的函数。要做到这一点，我们必须在所有的单元格、每个单元格中的粒子上循环，并评估每个粒子位置的电场。

// 这里使用的方法在概念上与`assemble_system()`函数中使用的相同。我们在所有单元中循环，找到位于那里的粒子（同样要注意这里用来寻找这些粒子的算法的低效率），并使用FEPointEvaluation对象来评估这些位置的梯度。

  template <int dim>
  void CathodeRaySimulator<dim>::move_particles()
  {
    const double dt = time.get_next_step_size();

    Vector<double>            solution_values(fe.n_dofs_per_cell());
    FEPointEvaluation<1, dim> evaluator(mapping, fe, update_gradients);

    for (const auto &cell : dof_handler.active_cell_iterators())
      if (particle_handler.n_particles_in_cell(cell) > 0)
        {
          const typename Particles::ParticleHandler<
            dim>::particle_iterator_range particles_in_cell =
            particle_handler.particles_in_cell(cell);

          std::vector<Point<dim>> particle_positions;
          for (const auto &particle : particles_in_cell)
            particle_positions.push_back(particle.get_reference_location());

          cell->get_dof_values(solution, solution_values);

// 然后，我们可以向FEPointEvaluation对象询问这些位置的解决方案的梯度（即电场 $\mathbf E$ ），并在各个粒子上循环。

          evaluator.reinit(cell, particle_positions);
          evaluator.evaluate(make_array_view(solution_values),
                             EvaluationFlags::gradients);

          {
            typename Particles::ParticleHandler<dim>::particle_iterator
              particle = particles_in_cell.begin();
            for (unsigned int particle_index = 0;
                 particle != particles_in_cell.end();
                 ++particle, ++particle_index)
              {
                const Tensor<1, dim> &E =
                  evaluator.get_gradient(particle_index);

// 现在我们已经得到了其中一个粒子位置的电场，我们首先用它来更新速度，然后更新位置。为此，我们首先从粒子的属性中获取旧的速度，计算加速度，更新速度，并将这个新的速度再次存储在粒子的属性中。回顾一下，这对应于介绍中所讨论的以下一组更新方程中的第一个。
      //  @f{align*}{
      //      \frac{{\mathbf v}_i^{(n)}
      //            -{\mathbf v}_i^{(n-1)}}{\Delta t}
      //      &= \frac{e\nabla V^{(n)}}{m}
      //   \\ \frac{{\mathbf x}_i^{(n)}-{\mathbf x}_i^{(n-1)}}
      //           {\Delta t} &= {\mathbf v}_i^{(n)}.
      //  @f}

                const Tensor<1, dim> old_velocity(particle->get_properties());

                const Tensor<1, dim> acceleration =
                  Constants::electron_charge / Constants::electron_mass * E;

                const Tensor<1, dim> new_velocity =
                  old_velocity + acceleration * dt;

                particle->set_properties(make_array_view(new_velocity));

// 有了新的速度，我们也就可以更新粒子的位置，并告诉粒子这个位置。

                const Point<dim> new_location =
                  particle->get_location() + dt * new_velocity;
                particle->set_location(new_location);
              }
          }
        }

// 在更新了所有粒子的位置和属性（即速度）之后，我们需要确保`particle_handler`再次知道它们在哪个单元中，以及它们在参考单元坐标系中的位置。下面的函数就是这样做的。(它还确保在并行计算中，如果粒子从一个处理器拥有的子域移动到另一个处理器拥有的子域，那么粒子会从一个处理器移动到另一个处理器。)

    particle_handler.sort_particles_into_subdomains_and_cells();
  }
// @sect4{CathodeRaySimulator::track_lost_particle}

// 最后一个与粒子相关的函数是当一个粒子从模拟中丢失时被调用的函数。这通常发生在它离开域的时候。如果发生这种情况，这个函数会同时调用单元（我们可以询问它的新位置）和它之前所在的单元。然后，该函数不断跟踪更新这个时间步骤中丢失的粒子数，丢失的粒子总数，然后估计该粒子是否通过阳极中间的孔离开。我们这样做，首先检查它最后所在的单元是否有一个 $x$ 坐标在右边边界的左边（位于 $x=4$ ），而粒子现在的位置在右边边界的右边。如果是这样的话，我们就计算出它的运动方向矢量，这个方向矢量被归一化了，所以方向矢量的 $x$ 分量等于 $1$  。有了这个方向矢量，我们可以计算出它与直线 $x=4$ 的相交位置。如果这个相交点在 $0.5$ 和 $1.5$ 之间，那么我们就声称粒子从孔中离开，并增加一个计数器。

  template <int dim>
  void CathodeRaySimulator<dim>::track_lost_particle(
    const typename Particles::ParticleIterator<dim> &        particle,
    const typename Triangulation<dim>::active_cell_iterator &cell)
  {
    ++n_recently_lost_particles;
    ++n_total_lost_particles;

    const Point<dim> current_location              = particle->get_location();
    const Point<dim> approximate_previous_location = cell->center();

    if ((approximate_previous_location[0] < 4) && (current_location[0] > 4))
      {
        const Tensor<1, dim> direction =
          (current_location - approximate_previous_location) /
          (current_location[0] - approximate_previous_location[0]);

        const double right_boundary_intercept =
          approximate_previous_location[1] +
          (4 - approximate_previous_location[0]) * direction[1];
        if ((right_boundary_intercept > 0.5) &&
            (right_boundary_intercept < 1.5))
          ++n_particles_lost_through_anode;
      }
  }

//  @sect4{CathodeRaySimulator::update_timestep_size}

// 正如在介绍中详细讨论的那样，我们需要尊重一个时间步长条件，即颗粒在一个时间步长中不能移动超过一个单元。为了确保这一点，我们首先计算每个单元上所有粒子的最大速度，然后用该速度除以单元大小。然后，我们使用介绍中讨论的安全系数，将下一个时间步长计算为所有单元上这个量的最小值，并使用 DiscreteTime::set_desired_time_step_size() 函数将其设定为所需的时间步长。

  template <int dim>
  void CathodeRaySimulator<dim>::update_timestep_size()
  {
    if (time.get_step_number() > 0)
      {
        double min_cell_size_over_velocity = std::numeric_limits<double>::max();

        for (const auto &cell : dof_handler.active_cell_iterators())
          if (particle_handler.n_particles_in_cell(cell) > 0)
            {
              const double cell_size = cell->minimum_vertex_distance();

              double max_particle_velocity(0.0);

              for (const auto &particle :
                   particle_handler.particles_in_cell(cell))
                {
                  const Tensor<1, dim> velocity(particle.get_properties());
                  max_particle_velocity =
                    std::max(max_particle_velocity, velocity.norm());
                }

              if (max_particle_velocity > 0)
                min_cell_size_over_velocity =
                  std::min(min_cell_size_over_velocity,
                           cell_size / max_particle_velocity);
            }

        constexpr double c_safety = 0.5;
        time.set_desired_next_step_size(c_safety * 0.5 *
                                        min_cell_size_over_velocity);
      }

// 正如在介绍中提到的，我们必须以不同的方式对待第一个时间步长，因为在那里，粒子还没有出现，或者还没有我们计算合理步长所需的相关信息。下面的公式遵循介绍中的讨论。

    else
      {
        const QTrapezoid<dim> vertex_quadrature;
        FEValues<dim> fe_values(fe, vertex_quadrature, update_gradients);

        std::vector<Tensor<1, dim>> field_gradients(vertex_quadrature.size());

        double min_timestep = std::numeric_limits<double>::max();

        for (const auto &cell : dof_handler.active_cell_iterators())
          if (particle_handler.n_particles_in_cell(cell) > 0)
            {
              const double cell_size = cell->minimum_vertex_distance();

              fe_values.reinit(cell);
              fe_values.get_function_gradients(solution, field_gradients);

              double max_E = 0;
              for (const auto q_point : fe_values.quadrature_point_indices())
                max_E = std::max(max_E, field_gradients[q_point].norm());

              if (max_E > 0)
                min_timestep =
                  std::min(min_timestep,
                           std::sqrt(0.5 * cell_size *
                                     Constants::electron_mass /
                                     Constants::electron_charge / max_E));
            }

        time.set_desired_next_step_size(min_timestep);
      }
  }

//  @sect4{The CathodeRaySimulator::output_results() function}

// 实现整个算法的最后一个函数是生成图形输出的函数。在目前的情况下，我们想同时输出电势场以及粒子的位置和速度。但我们也想输出电场，即解决方案的梯度。

// deal.II有一个一般的方法，可以从解决方案中计算出派生量，并输出这些量。在这里，这是电场，但也可以是其他的量--比如说，电场的法线，或者事实上任何其他人们想从解 $V_h(\mathbf x)$ 或其导数中计算的量。这个一般的解决方案使用了DataPostprocessor类，在像这里的情况下，我们想输出一个代表矢量场的量，则使用DataPostprocessorVector类。

// 与其尝试解释这个类是如何工作的，不如让我们简单地参考一下DataPostprocessorVector类的文档，这个案例基本上是一个有据可查的例子。

  template <int dim>
  class ElectricFieldPostprocessor : public DataPostprocessorVector<dim>
  {
  public:
    ElectricFieldPostprocessor()
      : DataPostprocessorVector<dim>("electric_field", update_gradients)
    {}

    virtual void evaluate_scalar_field(
      const DataPostprocessorInputs::Scalar<dim> &input_data,
      std::vector<Vector<double>> &computed_quantities) const override
    {
      AssertDimension(input_data.solution_gradients.size(),
                      computed_quantities.size());

      for (unsigned int p = 0; p < input_data.solution_gradients.size(); ++p)
        {
          AssertDimension(computed_quantities[p].size(), dim);
          for (unsigned int d = 0; d < dim; ++d)
            computed_quantities[p][d] = input_data.solution_gradients[p][d];
        }
    }
  };

// 有了这个，`output_results()`函数就变得相对简单了。我们使用DataOut类，就像我们在以前几乎所有的教程程序中使用的那样，来输出解决方案（"电动势"），我们使用上面定义的后处理程序来输出其梯度（"电场"）。这些都被写入一个VTU格式的文件中，同时将当前时间和时间步长与该文件联系起来。

  template <int dim>
  void CathodeRaySimulator<dim>::output_results() const
  {
    {
      ElectricFieldPostprocessor<dim> electric_field;
      DataOut<dim>                    data_out;
      data_out.attach_dof_handler(dof_handler);
      data_out.add_data_vector(solution, "electric_potential");
      data_out.add_data_vector(solution, electric_field);
      data_out.build_patches();

      data_out.set_flags(
        DataOutBase::VtkFlags(time.get_current_time(), time.get_step_number()));

      std::ofstream output("solution-" +
                           Utilities::int_to_string(time.get_step_number(), 4) +
                           ".vtu");
      data_out.write_vtu(output);
    }

// 输出粒子的位置和属性并不复杂。 Particles::DataOut 类扮演了粒子的DataOut类的角色，我们所要做的就是告诉该类从哪里获取粒子，以及如何解释属性中的`dim`分量--即作为表示速度的单一矢量，而不是作为`dim`标量属性。剩下的就和上面一样了。

    {
      Particles::DataOut<dim, dim> particle_out;
      particle_out.build_patches(
        particle_handler,
        std::vector<std::string>(dim, "velocity"),
        std::vector<DataComponentInterpretation::DataComponentInterpretation>(
          dim, DataComponentInterpretation::component_is_part_of_vector));

      particle_out.set_flags(
        DataOutBase::VtkFlags(time.get_current_time(), time.get_step_number()));

      std::ofstream output("particles-" +
                           Utilities::int_to_string(time.get_step_number(), 4) +
                           ".vtu");
      particle_out.write_vtu(output);
    }
  }
// @sect4{CathodeRaySimulator::run}

// 这个程序的主类的最后一个成员函数是驱动。在顶层，它通过在一连串越来越细的网格上求解问题（尚未创建粒子），对网格进行多次细化。

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

//在前面做几个细化循环

    const unsigned int n_pre_refinement_cycles = 3;
    for (unsigned int refinement_cycle = 0;
         refinement_cycle < n_pre_refinement_cycles;
         ++refinement_cycle)
      {
        setup_system();
        assemble_system();
        solve_field();
        refine_grid();
      }

// 现在进行时间上的循环。这个步骤的顺序紧跟介绍中讨论的算法大纲。正如在DiscreteTime类的文档中详细讨论的那样，虽然我们将场和粒子信息向前移动了一个时间步长，但存储在`time`变量中的时间与这些量的（部分）位置不一致（在DiscreteTime的字典中，这就是 "更新阶段"）。对`time.advance_time()`的调用通过将`time`变量设置为场和粒子已经处于的时间而使一切重新保持一致，一旦我们处于这个 "一致阶段"，我们就可以生成图形输出并将模拟的当前状态的信息写入屏幕。

    setup_system();
    do
      {
        std::cout << "Timestep " << time.get_step_number() + 1 << std::endl;
        std::cout << "  Field degrees of freedom:                 "
                  << dof_handler.n_dofs() << std::endl;

        assemble_system();
        solve_field();

        create_particles();
        std::cout << "  Total number of particles in simulation:  "
                  << particle_handler.n_global_particles() << std::endl;

        n_recently_lost_particles = 0;
        update_timestep_size();
        move_particles();

        time.advance_time();

        output_results();

        std::cout << "  Number of particles lost this time step:  "
                  << n_recently_lost_particles << std::endl;
        if (n_total_lost_particles > 0)
          std::cout << "  Fraction of particles lost through anode: "
                    << 1. * n_particles_lost_through_anode /
                         n_total_lost_particles
                    << std::endl;

        std::cout << std::endl
                  << "  Now at t=" << time.get_current_time()
                  << ", dt=" << time.get_previous_step_size() << '.'
                  << std::endl
                  << std::endl;
      }
    while (time.is_at_end() == false);
  }
} // namespace Step19

//  @sect3{The main function}

// 程序的最后一个函数又是`main()`函数。自 step-6 以来，它在所有的教程程序中都没有变化，因此没有什么新的内容需要讨论。

int main()
{
  try
    {
      Step19::CathodeRaySimulator<2> cathode_ray_simulator_2d;
      cathode_ray_simulator_2d.run();
    }
  catch (std::exception &exc)
    {
      std::cerr << std::endl
                << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Exception on processing: " << std::endl
                << exc.what() << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;

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


/*
 *****************************************************************
 *                     String Toolkit Library                    *
 *                                                               *
 * Random Line Selection                                         *
 * Author: Arash Partow (2002-2018)                              *
 * URL: http://www.partow.net/programming/strtk/index.html       *
 *                                                               *
 * Copyright notice:                                             *
 * Free use of the String Toolkit Library is permitted under the *
 * guidelines and in accordance with the most current version of *
 * the MIT License.                                              *
 * http://www.opensource.org/licenses/MIT                        *
 *                                                               *
 *****************************************************************
*/

/*
   Description: This is a solution to the problem of randomly selecting a line
                from a text file in the most efficient way possible taking into
                account time and space complexities, also ensuring that the
                probability of the line selected is exactly 1/N where N is the
                number of lines in the text file - It should be noted that the
                lines can be of varying length.
*/

#include <cstddef>
#include <iostream>
#include <iterator>
#include <string>
#include <deque>
#include <ctime>

#include <boost/random.hpp>
//#include <random>

#include "strtk.hpp"

#ifndef strtk_enable_random
   #error This example requires random
#endif

class random_line_selector
{
public:

   random_line_selector(std::string& line, const std::size_t& seed = 0xA5A5A5A5)
   : line_count_(1),
     line_(line),
     rng_(seed)
   {}

   inline void operator()(const std::string& s)
   {
      if (rng_() < (1.0 / line_count_))
         line_ = s;
      ++line_count_;
   }

private:

   random_line_selector operator=(const random_line_selector&);

   std::size_t line_count_; // should be long long
   std::string& line_;
   strtk::uniform_real_rng rng_;
};

int main(int argc, char* argv[])
{
   if (2 != argc)
   {
      std::cout << "usage: strtk_random_line <file name>" << std::endl;
      return 1;
   }

   std::string file_name = argv[1];
   std::string line;

   strtk::for_each_line(file_name,
                        random_line_selector(line,static_cast<std::size_t>(::time(0))));

   std::cout << line << std::endl;

   return 0;
}