#ifndef STAN_MATH_PRIM_FUN_INV_INC_BETA_HPP
#define STAN_MATH_PRIM_FUN_INV_INC_BETA_HPP

#include <stan/math/prim/meta.hpp>
#include <stan/math/prim/err.hpp>
#include <stan/math/prim/fun/boost_policy.hpp>
#include <boost/math/special_functions/beta.hpp>

namespace stan {
namespace math {

/**
 * The inverse of the normalized incomplete beta function of a, b, with
 * probability p.
 *
 * Used to compute the inverse cumulative density function for the beta
 * distribution.
 *
 * @param a Shape parameter a >= 0; a and b can't both be 0
 * @param b Shape parameter b >= 0
 * @param p Random variate. 0 <= p <= 1
 * @throws if constraints are violated or if any argument is NaN
 * @return The inverse of the normalized incomplete beta function.
 */
inline double inv_inc_beta(double a, double b, double p) {
  check_not_nan("inv_inc_beta", "a", a);
  check_not_nan("inv_inc_beta", "b", b);
  check_not_nan("inv_inc_beta", "p", p);
  check_positive("inv_inc_beta", "a", a);
  check_positive("inv_inc_beta", "b", b);
  check_bounded("inv_inc_beta", "p", p, 0, 1);
  return boost::math::ibeta_inv(a, b, p, boost_policy_t<>());
}

}  // namespace math
}  // namespace stan
#endif


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

/*
 Copyright (C) 2014 Peter Caspers

 This file is part of QuantLib, a free-software/open-source library
 for financial quantitative analysts and developers - http://quantlib.org/

 QuantLib is free software: you can redistribute it and/or modify it
 under the terms of the QuantLib license.  You should have received a
 copy of the license along with this program; if not, please email
 <quantlib-dev@lists.sf.net>. The license is also available online at
 <http://quantlib.org/license.shtml>.

 This program 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 license for more details.
*/

#include <ql/experimental/finitedifferences/fdmdupire1dop.hpp>
#include <ql/methods/finitedifferences/operators/secondderivativeop.hpp>
#include <boost/numeric/ublas/matrix.hpp>

namespace QuantLib {

FdmDupire1dOp::FdmDupire1dOp(const ext::shared_ptr<FdmMesher> &mesher,
                             const Array &localVolatility)
    : mesher_(mesher), localVolatility_(localVolatility),
      mapT_(SecondDerivativeOp(0, mesher)
                .mult(0.5 * localVolatility * localVolatility)) {}

void FdmDupire1dOp::setTime(Time t1, Time t2) {}

Size FdmDupire1dOp::size() const { return 1; }

Array FdmDupire1dOp::apply(const Array &u) const {
    return mapT_.apply(u);
}

Array FdmDupire1dOp::apply_direction(Size direction, const Array &r) const {
    if (direction == 0)
        return mapT_.apply(r);
    QL_FAIL("direction too large");
}

Array FdmDupire1dOp::apply_mixed(const Array &r) const {
    return r;
}

Array FdmDupire1dOp::solve_splitting(Size direction, const Array &r, Real a) const {
    if (direction == 0) {
        return mapT_.solve_splitting(r, a, 1.0);
    }
    QL_FAIL("direction too large");
}

Array FdmDupire1dOp::preconditioner(const Array &r, Real dt) const {

    return solve_splitting(0, r, dt);
}

std::vector<SparseMatrix> FdmDupire1dOp::toMatrixDecomp() const {
    return std::vector<SparseMatrix>(1, mapT_.toMatrix());
}

}


#include <cassert>

#include <dlib/svm.h>
#include <dlib/svm_threaded.h>
#include <dlib/global_optimization.h>

#include "common.hpp"
#include "data.hpp"

namespace SAMPML_NAMESPACE {
    namespace trainer {
        template <class sample_type>
        class svm_classifier {
        public:
            svm_classifier() { }

            template <class SampleContainerPositive, class SampleContainerNegative>
            void set_samples(const SampleContainerPositive& positives, const SampleContainerNegative& negatives) {
                static_assert(std::is_same<typename SampleContainerPositive::value_type, sample_type>());
                static_assert(std::is_same<typename SampleContainerNegative::value_type, sample_type>());

                samples.clear();
                samples.insert(samples.end(), positives.cbegin(), positives.cend());
                samples.insert(samples.end(), negatives.cbegin(), negatives.cend());
                labels.clear();
                labels.insert(labels.end(), positives.size(), +1);
                labels.insert(labels.end(), negatives.size(), -1);
            }

            void train (double beta_squared = 1.0) {
                if(samples.size() == 0) {
                    throw bad_input("Bad Input: no samples were provided for training");
                }
                assert(labels.size() == samples.size());

                dlib::vector_normalizer<sample_type> normalizer;
                normalizer.train(samples);
                for(auto& vector : samples)
                    vector = normalizer(vector);

                dlib::randomize_samples(samples, labels);

                auto cross_validation_score =
                [this, beta_squared](double gamma, double cost_positive, double cost_negative) {
                    dlib::svm_c_trainer<svm_kernel_type> trainer;
                    trainer.set_kernel(svm_kernel_type(gamma));
                    trainer.set_c_class1(cost_positive);
                    trainer.set_c_class2(cost_negative);

                    dlib::matrix<double> result = dlib::cross_validate_trainer(trainer, this->samples, this->labels, 10);
                    std::cout << "gamma: " << gamma << "  c1: " << cost_positive <<  "  c2: " << cost_negative <<  "  cross validation accuracy: " << result;

                    return (1.0 + beta_squared)*dlib::prod(result)/(beta_squared * result(0) + result(1));
                };

                auto result = dlib::find_max_global(dlib::default_thread_pool(),
                                                    cross_validation_score,
                                                    {1e-5, 1e-5, 1e-5}, {100,  1e6,  1e6},
                                                    dlib::max_function_calls(50));

                best_gamma = result.x(0);
                best_cost_positive = result.x(1);
                best_cost_negative = result.x(2);

                dlib::svm_c_trainer<svm_kernel_type> trainer;
                trainer.set_kernel(svm_kernel_type(best_gamma));
                trainer.set_c_class1(best_cost_positive);
                trainer.set_c_class2(best_cost_negative);

                classifier.normalizer = normalizer;
                classifier.function = dlib::train_probabilistic_decision_function(trainer, samples, labels, 3);
            }

            auto rank_features() {
                dlib::kcentroid<svm_kernel_type> kc(svm_kernel_type(best_gamma), 0.0001);
                return dlib::rank_features(kc, samples, labels);
            }

            void serialize(std::string classifier) {
                dlib::serialize(classifier) << this->classifier;
            }

            void deserialize(std::string classifier) {
                dlib::deserialize(classifier) >> this->classifier;
            }

            double test(const sample_type& sample) {
                return classifier(sample);
            }

        protected:
            std::vector<sample_type> samples;
            std::vector<double> labels;

            using svm_kernel_type = dlib::radial_basis_kernel<sample_type>;
            using decision_funct_type = dlib::probabilistic_decision_function<svm_kernel_type>;
            using normalized_decision_funct_type = dlib::normalized_function<decision_funct_type>;
            normalized_decision_funct_type classifier;

            double best_gamma;
            double best_cost_positive;
            double best_cost_negative;
        };
    }
}


#include <string>
#include <Eigen/Dense>

#include <gtest/gtest.h>

namespace dr {

/// Convert an eigen vector to a terser string.
std::string toString(Eigen::Vector3d const & v) {
	return "[" + std::to_string(v[0]) + ", " + std::to_string(v[1]) + ", " + std::to_string(v[2]) + "]";
}

std::string toString(Eigen::AngleAxisd const & a) {
	Eigen::Vector3d axis = a.axis();
	return "[" + std::to_string(axis[0]) + ", " + std::to_string(axis[1]) + ", " +
		std::to_string(axis[2]) + ", " + std::to_string(a.angle()) + "]";
}

std::string toString(Eigen::Isometry3d const & a) {
	return toString(a.translation()) + toString(Eigen::AngleAxisd(a.rotation()));
}

/// Test if two Eigen vectors are exactly equal.
testing::AssertionResult testEqual(Eigen::Vector3d const & expected, Eigen::Vector3d const & actual) {
	if (expected.x() == actual.x() && expected.y() == actual.y() && expected.z() == actual.z()) {
		return testing::AssertionSuccess();
	} else {
		return testing::AssertionFailure() << "actual (" << toString(actual) << ") does not equal expected (" << toString(expected) << ")";
	}
}

/// Test if two Eigen vectors are within tolerance of eachother.
testing::AssertionResult testNear(Eigen::Vector3d const & expected, Eigen::Vector3d const & actual, Eigen::Vector3d const & tolerance = {0.001, 0.001, 0.001}) {
	auto diff = (expected - actual).cwiseAbs();
	if ((diff.array() <= tolerance.array()).all()) {
		return testing::AssertionSuccess();
	} else {
		return testing::AssertionFailure() << "actual (" << toString(actual) << ") is not within tolerance (" << toString(tolerance) << ") of expected (" << toString(expected) << ")";
	}
}

/// Test if two Eigen rotation axes are within tolerance of eachother.
testing::AssertionResult testNear(Eigen::AngleAxisd const & expected, Eigen::AngleAxisd const & actual, float tolerance = 0.001) {
	if (actual.isApprox(expected, tolerance)) {
		return testing::AssertionSuccess();
	} else {
		return testing::AssertionFailure() << "actual (" << toString(actual) << ") is not within tolerance (" << std::to_string(tolerance) << ") of expected (" << toString(expected) << ")";
	}
}

/// Test if two Eigen isometries are within tolerance of each other.
testing::AssertionResult testNear(Eigen::Isometry3d const & expected, Eigen::Isometry3d const & actual, float tolerance = 0.001) {
	if (
		testNear(Eigen::Vector3d(expected.translation()), Eigen::Vector3d(actual.translation()), Eigen::Vector3d(tolerance, tolerance, tolerance)) &&
		testNear(Eigen::AngleAxisd(Eigen::Quaterniond(expected.rotation())), Eigen::AngleAxisd(Eigen::Quaterniond(actual.rotation())), tolerance)
	) {
		return testing::AssertionSuccess();
	} else {
		return testing::AssertionFailure() << "actual (" << toString(actual) << ") is not within tolerance (" << std::to_string(tolerance) << ") of expected (" << toString(expected) << ")";
	}
}

}


#include <iostream>
#include <vector>
#include <string>
#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>
#include <cstdlib>
#include <cmath>
#include <cstdio>
using namespace std;

// 合計
double sum(vector<double> n){
  double res = 0;
  BOOST_FOREACH(double m, n)
    res += m;
  return res;
}

// 平均
double average(vector<double> n){
  return sum(n)/n.size();
}

// 自乗平均
double saverage(vector<double> n){
  BOOST_FOREACH(double& m, n)
    m *= m;
  return average(n)/n.size();
}

// 分散
double variance(vector<double> n){
  vector<double> data;
  double a = average(n);
  BOOST_FOREACH(double m, n)
    data.push_back(m-a);
  return saverage(data);
}

int main(void){
  vector<vector<double>> dataset;

  while(true){
    string input;
    vector<string> strs;

    cin >> input;
    if(input == "0") break;

    getline(cin, input);
    boost::split(strs, input, boost::is_space());
    BOOST_FOREACH(string m, strs) cout << m << endl;
    cout << endl;
    vector<double> d;
    BOOST_FOREACH(string s, strs){
      d.push_back(atoi(s.c_str()));
    }
    dataset.push_back(d);
  }

  BOOST_FOREACH(vector<double> ds, dataset){
    cout << sqrt(variance(ds)) << endl;
  }
}


#include <iostream>>
#include <Eigen\Dense>

using namespace Eigen;
using namespace std;

void main() {
	{
		MatrixXi mat(3, 3);
		mat << 1, 2, 3, 4, 5, 6, 7, 8, 9;
		cout << "mat = \n" << mat << endl;

		// This assignment shows the aliasing problem
		mat.bottomRightCorner(2, 2) = mat.topLeftCorner(2, 2);
		cout << "After the assignment, mat =\n" << mat << endl;
		cout << "--> The aliasing problem" << endl << endl;
	}
	{
		Matrix2i a; a << 1, 2, 3, 4;
		cout << "Matrix a =\n" << a << endl;
		//a = a.transpose(); // !!! do NOT do this !!!
		//cout << "Another aliasing problem with a = a.tranpose()." << endl;
	}
	cout << "------------" << endl;
	cout << "Resolving aliasing issues" << endl;
	{
		MatrixXi mat(3, 3);
		mat << 1, 2, 3, 4, 5, 6, 7, 8, 9;
		cout << "mat = \n" << mat << endl;
		mat.bottomRightCorner(2, 2) = mat.topLeftCorner(2, 2).eval();
		cout << "After the assignment with .eval(), mat = \n" << mat << endl;
	}
	{
		Matrix2i a; a << 1, 2, 3, 4;
		cout << "Matrix a =\n" << a << endl;
		a.transposeInPlace();
		cout << "After the transposeInPlace(), a = \n" << a << endl;
	}
	cout << "-------------" << endl;
	cout << "Aliasing and component-wise operations" << endl;
	{
		MatrixXf mat(2, 2);
		mat << 1, 2, 4, 7;
		cout << "mat = \n" << mat << endl;
		mat = 2 * mat;
		cout << "after mat = 2 * mat \n" << mat << endl;
		mat = mat - MatrixXf::Identity(2, 2);
		cout << "After the subtraction: - Identity(2,2)\n" << mat << endl;

		ArrayXXf arr = mat;
		arr = arr.square();
		cout << "After squaring \n" << arr << endl;

		mat << 1, 2, 4, 7;
		mat = (2 * mat - MatrixXf::Identity(2, 2)).array().square();
		cout << "Doing everything at once \n" << mat << endl;
	}

	{
		cout << "-------------" << endl;
		cout << "In matrix multiplication, Eigen assumes aliasing by default under the condition that the dest matrix is not resized" << endl;
		MatrixXf matA(2, 2);
		matA << 2, 0, 0, 2;
		matA = matA * matA;
		cout << "matA=\n" << matA << endl;
		cout << "---> Eigen evaluates the product in a temporary matrix" << endl;
	}
	{
		cout << "---> Use .noalias() to indicate there is no aliasing" << endl;
		MatrixXf matA(2, 2), matB(2, 2);
		matA << 2, 0, 0, 2;
		// Simple but not quite as efficient
		matB = matA * matA;
		cout << matB << endl << endl;
		// More complicated but also more efficient
		matB.noalias() = matA * matA;
		cout << matB << endl;
	}
	{
		cout << "aliasing is NOT assumsed if the dest matrix is resized" << endl;
		{
			MatrixXf A(2, 2), B(3, 2);
			B << 2, 0, 0, 3, 1, 1;
			A << 2, 0, 0, -2;
			cout << "B* A\n" << B* A << endl;
			A = (B * A).cwiseAbs(); // aliasing problem
			cout << A << endl;
		}
		{
			MatrixXf A(2, 2), B(3, 2);
			B << 2, 0, 0, 3, 1, 1;
			A << 2, 0, 0, -2;
			A = (B * A).eval().cwiseAbs();
			cout << A << endl;
		}
	}

	system("pause");
}


/* ---------------------------------------------------------------------
 *
 * Copyright (C) 2018 - 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 at
 * the top level of the deal.II distribution.
 *
 * ---------------------------------------------------------------------
 *
 * Author: Daniel Garcia-Sanchez, CNRS, 2019
 */

// @sect3{Include files}

// 我们在这个程序中需要的大部分包含文件已经在以前的程序中讨论过了，特别是在  step-40  .

#include <deal.II/base/conditional_ostream.h>
#include <deal.II/base/function.h>

#include <deal.II/base/index_set.h>
#include <deal.II/base/quadrature_lib.h>
#include <deal.II/base/timer.h>
#include <deal.II/base/utilities.h>

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

#include <deal.II/fe/fe_q.h>
#include <deal.II/fe/fe_system.h>
#include <deal.II/fe/fe_values.h>

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

#include <deal.II/lac/affine_constraints.h>
#include <deal.II/lac/dynamic_sparsity_pattern.h>
#include <deal.II/lac/full_matrix.h>
#include <deal.II/lac/generic_linear_algebra.h>
#include <deal.II/lac/petsc_solver.h>
#include <deal.II/lac/vector.h>

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

#include <fstream>
#include <iostream>

// 下面的标头提供了我们用来表示材料属性的张量类。

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

// 下面的标头对于deal.II的HDF5接口是必要的。

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

// 这个头是我们用来评估模拟结果的函数 VectorTools::point_value 所需要的。

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

// 我们在函数 GridTools::find_active_cell_around_point 中使用的函数 `ElasticWave::store_frequency_step_data()` 需要这些头文件。
#include <deal.II/grid/grid_tools.h>
#include <deal.II/grid/grid_tools_cache.h>

namespace step62
{
  using namespace dealii;
// @sect3{Auxiliary classes and functions}  下列类用于存储模拟的参数。

//  @sect4{The `RightHandSide` class}  该类用于定义结构左侧的力脉冲。

  template <int dim>
  class RightHandSide : public Function<dim>
  {
  public:
    RightHandSide(HDF5::Group &data);

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

  private:

// 变量`data`是 HDF5::Group ，所有的模拟结果都将被储存在其中。请注意， `RightHandSide::data`, 变量
// `PML::data`,
// `Rho::data` 和 `Parameters::data` 指向HDF5文件的同一个组。当 HDF5::Group 被复制时，它将指向HDF5文件的同一组。

    HDF5::Group data;

// 仿真参数作为HDF5属性存储在`data`中。以下属性在jupyter笔记本中定义，作为HDF5属性存储在`data`中，然后由构造函数读取。

    const double     max_force_amplitude;
    const double     force_sigma_x;
    const double     force_sigma_y;
    const double     max_force_width_x;
    const double     max_force_width_y;
    const Point<dim> force_center;

  public:

// 在这个特定的模拟中，力只有一个 $x$ 分量， $F_y=0$  。

    const unsigned int force_component = 0;
  };
// @sect4{The `PML` class}  这个类是用来定义完美匹配层（PML）的形状，以吸收向边界传播的波。

  template <int dim>
  class PML : public Function<dim, std::complex<double>>
  {
  public:
    PML(HDF5::Group &data);

    virtual std::complex<double>
    value(const Point<dim> &p, const unsigned int component) const override;

  private:
// HDF5::Group ，所有的模拟结果将被存储在其中。

    HDF5::Group data;

// 和以前一样，以下属性在jupyter笔记本中定义，作为HDF5属性存储在`data`中，然后由构造函数读取。

    const double pml_coeff;
    const int    pml_coeff_degree;
    const double dimension_x;
    const double dimension_y;
    const bool   pml_x;
    const bool   pml_y;
    const double pml_width_x;
    const double pml_width_y;
    const double a_coeff_x;
    const double a_coeff_y;
  };

//  @sect4{The `Rho` class}  这个类是用来定义质量密度的。

  template <int dim>
  class Rho : public Function<dim>
  {
  public:
    Rho(HDF5::Group &data);

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

  private:
// HDF5::Group ，所有的模拟结果将被存储在其中。

    HDF5::Group data;

// 和以前一样，以下属性在jupyter笔记本中定义，作为HDF5属性存储在`data`中，然后由构造函数读取。

    const double       lambda;
    const double       mu;
    const double       material_a_rho;
    const double       material_b_rho;
    const double       cavity_resonance_frequency;
    const unsigned int nb_mirror_pairs;
    const double       dimension_y;
    const unsigned int grid_level;
    double             average_rho_width;
  };

//  @sect4{The `Parameters` class}  该类包含所有将在模拟中使用的参数。

  template <int dim>
  class Parameters
  {
  public:
    Parameters(HDF5::Group &data);
// HDF5::Group ，所有的模拟结果将被存储在其中。

    HDF5::Group data;

// 和以前一样，以下属性在jupyter笔记本中定义，作为HDF5属性存储在`data`中，然后由构造函数读取。

    const std::string        simulation_name;
    const bool               save_vtu_files;
    const double             start_frequency;
    const double             stop_frequency;
    const unsigned int       nb_frequency_points;
    const double             lambda;
    const double             mu;
    const double             dimension_x;
    const double             dimension_y;
    const unsigned int       nb_probe_points;
    const unsigned int       grid_level;
    const Point<dim>         probe_start_point;
    const Point<dim>         probe_stop_point;
    const RightHandSide<dim> right_hand_side;
    const PML<dim>           pml;
    const Rho<dim>           rho;

  private:
    const double comparison_float_constant = 1e-12;
  };

//  @sect4{The `QuadratureCache` class}  质量和刚度矩阵的计算是非常昂贵的。这些矩阵对所有的频率步骤都是一样的。右手边的向量对所有的频率步长也是一样的。我们用这个类来存储这些对象，并在每个频率步骤中重新使用它们。请注意，这里我们不存储集合的质量和刚度矩阵以及右手边，而是存储单个单元的数据。QuadratureCache "类与在  step-18  中使用过的 "PointHistory "类非常相似。

  template <int dim>
  class QuadratureCache
  {
  public:
    QuadratureCache(const unsigned int dofs_per_cell);

  private:
    unsigned int dofs_per_cell;

  public:

// 我们在变量mass_coefficient和stiffness_coefficient中存储质量和刚度矩阵。我们还存储了右手边和JxW值，这些值对所有的频率步骤都是一样的。

    FullMatrix<std::complex<double>>  mass_coefficient;
    FullMatrix<std::complex<double>>  stiffness_coefficient;
    std::vector<std::complex<double>> right_hand_side;
    double                            JxW;
  };

//  @sect4{The `get_stiffness_tensor()` function}

// 该函数返回材料的刚度张量。为了简单起见，我们认为刚度是各向同性和同质的；只有密度  $\rho$  取决于位置。正如我们之前在  step-8  中所表明的，如果刚度是各向同性和均质的，那么刚度系数  $c_{ijkl}$  可以表示为两个系数  $\lambda$  和  $\mu$  的函数。系数张量简化为
// @f[
//    c_{ijkl}
//    =
//    \lambda \delta_{ij} \delta_{kl} +
//    \mu (\delta_{ik} \delta_{jl} + \delta_{il} \delta_{jk}).
//  @f] 。

  template <int dim>
  SymmetricTensor<4, dim> get_stiffness_tensor(const double lambda,
                                               const double mu)
  {
    SymmetricTensor<4, dim> stiffness_tensor;
    for (unsigned int i = 0; i < dim; ++i)
      for (unsigned int j = 0; j < dim; ++j)
        for (unsigned int k = 0; k < dim; ++k)
          for (unsigned int l = 0; l < dim; ++l)
            stiffness_tensor[i][j][k][l] =
              (((i == k) && (j == l) ? mu : 0.0) +
               ((i == l) && (j == k) ? mu : 0.0) +
               ((i == j) && (k == l) ? lambda : 0.0));
    return stiffness_tensor;
  }

//  @sect3{The `ElasticWave` class}

// 接下来让我们声明这个程序的主类。它的结构与 step-40 的教程程序非常相似。主要的区别是。

// - 扫过的频率值。

// - 我们将刚度和质量矩阵保存在`quadrature_cache`中，并在每个频率步骤中使用它们。

// - 我们在HDF5文件中存储每个频率步骤的探头测量的能量。

  template <int dim>
  class ElasticWave
  {
  public:
    ElasticWave(const Parameters<dim> &parameters);
    void run();

  private:
    void setup_system();
    void assemble_system(const double omega,
                         const bool   calculate_quadrature_data);
    void solve();
    void initialize_probe_positions_vector();
    void store_frequency_step_data(const unsigned int frequency_idx);
    void output_results();

// 在每个频率步骤之前都会调用这个，以便为缓存变量设置一个原始状态。

    void setup_quadrature_cache();

// 这个函数在频率向量上循环，并在每个频率步骤上运行模拟。

    void frequency_sweep();

// 参数存储在这个变量中。

    Parameters<dim> parameters;

    MPI_Comm mpi_communicator;

    parallel::distributed::Triangulation<dim> triangulation;

    QGauss<dim> quadrature_formula;

// 我们将每个单元的质量和刚度矩阵存储在这个向量中。

    std::vector<QuadratureCache<dim>> quadrature_cache;

    FESystem<dim>   fe;
    DoFHandler<dim> dof_handler;

    IndexSet locally_owned_dofs;
    IndexSet locally_relevant_dofs;

    AffineConstraints<std::complex<double>> constraints;

    LinearAlgebraPETSc::MPI::SparseMatrix system_matrix;
    LinearAlgebraPETSc::MPI::Vector       locally_relevant_solution;
    LinearAlgebraPETSc::MPI::Vector       system_rhs;

// 这个向量包含我们要模拟的频率范围。

    std::vector<double> frequency;

// 这个向量包含了测量探头各点的坐标 $(x,y)$ 。

    FullMatrix<double> probe_positions;

// HDF5数据集来存储频率和`探头位置`向量。

    HDF5::DataSet frequency_dataset;
    HDF5::DataSet probe_positions_dataset;

// HDF5数据集，存储探头测量的能量值。

    HDF5::DataSet displacement;

    ConditionalOStream pcout;
    TimerOutput        computing_timer;
  };

//  @sect3{Implementation of the auxiliary classes}
// @sect4{The `RightHandSide` class implementation}

// 构造函数使用 HDF5::Group  `data`函数从 HDF5::Group::get_attribute()  读取所有参数。

  template <int dim>
  RightHandSide<dim>::RightHandSide(HDF5::Group &data)
    : Function<dim>(dim)
    , data(data)
    , max_force_amplitude(data.get_attribute<double>("max_force_amplitude"))
    , force_sigma_x(data.get_attribute<double>("force_sigma_x"))
    , force_sigma_y(data.get_attribute<double>("force_sigma_y"))
    , max_force_width_x(data.get_attribute<double>("max_force_width_x"))
    , max_force_width_y(data.get_attribute<double>("max_force_width_y"))
    , force_center(Point<dim>(data.get_attribute<double>("force_x_pos"),
                              data.get_attribute<double>("force_y_pos")))
  {}

//这个函数定义了力矢量脉冲的空间形状，它采取高斯函数
// @f{align*}
//  F_x &=
//  \left\{
//  \begin{array}{ll}
//    a \exp(- (\frac{(x-b_x)^2 }{ 2 \sigma_x^2}+\frac{(y-b_y)^2 }{ 2
//    \sigma_y^2}))
//  & \text{if}\, x_\textrm{min} <x<x_\textrm{max}\, \text{and}\,
//  y_\textrm{min} <y<y_\textrm{max}  \\ 0 & \text{otherwise},
//  \end{array}
//  \right.\\ F_y &= 0
//  @f}
//  的形式，其中 $a$ 是取力的最大振幅， $\sigma_x$ 和 $\sigma_y$ 是 $x$ 和 $y$ 分量的标准偏差。请注意，脉冲已被裁剪为 $x_\textrm{min}<x<x_\textrm{max}$ 和 $y_\textrm{min} <y<y_\textrm{max}$  。

  template <int dim>
  double RightHandSide<dim>::value(const Point<dim> & p,
                                   const unsigned int component) const
  {
    if (component == force_component)
      {
        if (std::abs(p[0] - force_center[0]) < max_force_width_x / 2 &&
            std::abs(p[1] - force_center[1]) < max_force_width_y / 2)
          {
            return max_force_amplitude *
                   std::exp(-(std::pow(p[0] - force_center[0], 2) /
                                (2 * std::pow(force_sigma_x, 2)) +
                              std::pow(p[1] - force_center[1], 2) /
                                (2 * std::pow(force_sigma_y, 2))));
          }
        else
          {
            return 0;
          }
      }
    else
      {
        return 0;
      }
  }

//  @sect4{The `PML` class implementation}

// 和以前一样，构造函数使用 HDF5::Group 函数从 HDF5::Group::get_attribute() `data`中读取所有参数。正如我们所讨论的，在jupyter笔记本中已经定义了PML的二次开机。通过改变参数`pml_coeff_degree`，可以使用线性、立方或其他幂度。参数`pml_x`和`pml_y`可以用来开启和关闭`x`和`y`PML。

  template <int dim>
  PML<dim>::PML(HDF5::Group &data)
    : Function<dim, std::complex<double>>(dim)
    , data(data)
    , pml_coeff(data.get_attribute<double>("pml_coeff"))
    , pml_coeff_degree(data.get_attribute<int>("pml_coeff_degree"))
    , dimension_x(data.get_attribute<double>("dimension_x"))
    , dimension_y(data.get_attribute<double>("dimension_y"))
    , pml_x(data.get_attribute<bool>("pml_x"))
    , pml_y(data.get_attribute<bool>("pml_y"))
    , pml_width_x(data.get_attribute<double>("pml_width_x"))
    , pml_width_y(data.get_attribute<double>("pml_width_y"))
    , a_coeff_x(pml_coeff / std::pow(pml_width_x, pml_coeff_degree))
    , a_coeff_y(pml_coeff / std::pow(pml_width_y, pml_coeff_degree))
  {}

// `x`部分的PML系数的形式为  $s'_x = a_x x^{\textrm{degree}}$  。
  template <int dim>
  std::complex<double> PML<dim>::value(const Point<dim> & p,
                                       const unsigned int component) const
  {
    double calculated_pml_x_coeff = 0;
    double calculated_pml_y_coeff = 0;

    if ((component == 0) && pml_x)
      {
        const double pml_x_start_position = dimension_x / 2 - pml_width_x;
        if (std::abs(p[0]) > pml_x_start_position)
          {
            const double x_prime = std::abs(p[0]) - pml_x_start_position;
            calculated_pml_x_coeff =
              a_coeff_x * std::pow(x_prime, pml_coeff_degree);
          }
      }

    if ((component == 1) && pml_y)
      {
        const double pml_y_start_position = dimension_y / 2 - pml_width_y;
        if (std::abs(p[1]) > pml_y_start_position)
          {
            const double y_prime = std::abs(p[1]) - pml_y_start_position;
            calculated_pml_y_coeff =
              a_coeff_y * std::pow(y_prime, pml_coeff_degree);
          }
      }

    return 1. + std::max(calculated_pml_x_coeff, calculated_pml_y_coeff) *
                  std::complex<double>(0., 1.);
  }

//  @sect4{The `Rho` class implementation}

// 这个类是用来定义质量密度的。正如我们之前所解释的，一个声学超晶格空腔是由两个[分布式反射器](https:en.wikipedia.org/wiki/Band_gap)、镜子和一个 $\lambda/2$ 空腔组成的，其中 $\lambda$ 是声波长。声学DBRs是一种周期性结构，其中一组具有对比性物理特性（声速指数）的双层堆栈被重复 $N$ 次。波速的变化是由具有不同密度的层交替产生的。

  template <int dim>
  Rho<dim>::Rho(HDF5::Group &data)
    : Function<dim>(1)
    , data(data)
    , lambda(data.get_attribute<double>("lambda"))
    , mu(data.get_attribute<double>("mu"))
    , material_a_rho(data.get_attribute<double>("material_a_rho"))
    , material_b_rho(data.get_attribute<double>("material_b_rho"))
    , cavity_resonance_frequency(
        data.get_attribute<double>("cavity_resonance_frequency"))
    , nb_mirror_pairs(data.get_attribute<int>("nb_mirror_pairs"))
    , dimension_y(data.get_attribute<double>("dimension_y"))
    , grid_level(data.get_attribute<int>("grid_level"))
  {

// 为了提高精度，我们使用[subpixel smoothing]（https:meep.readthedocs.io/en/latest/Subpixel_Smoothing/）。

    average_rho_width = dimension_y / (std::pow(2.0, grid_level));
    data.set_attribute("average_rho_width", average_rho_width);
  }

  template <int dim>
  double Rho<dim>::value(const Point<dim> &p,
                         const unsigned int /*component*/) const
  {

// 声速由
// @f[
//   c = \frac{K_e}{\rho}
//  @f]
//  定义，其中 $K_e$ 是有效弹性常数， $\rho$ 是密度。这里我们考虑的是波导宽度远小于波长的情况。在这种情况下，可以证明对于二维的情况
//  @f[
//   K_e = 4\mu\frac{\lambda +\mu}{\lambda+2\mu}
//  @f]
//  和三维的情况 $K_e$ 等于杨氏模量。
//  @f[
//   K_e = \mu\frac{3\lambda +2\mu}{\lambda+\mu}
//  @f]

    double elastic_constant;
    if (dim == 2)
      {
        elastic_constant = 4 * mu * (lambda + mu) / (lambda + 2 * mu);
      }
    else if (dim == 3)
      {
        elastic_constant = mu * (3 * lambda + 2 * mu) / (lambda + mu);
      }
    else
      {
        Assert(false, ExcInternalError());
      }
    const double material_a_speed_of_sound =
      std::sqrt(elastic_constant / material_a_rho);
    const double material_a_wavelength =
      material_a_speed_of_sound / cavity_resonance_frequency;
    const double material_b_speed_of_sound =
      std::sqrt(elastic_constant / material_b_rho);
    const double material_b_wavelength =
      material_b_speed_of_sound / cavity_resonance_frequency;

//密度 $\rho$ 采取以下形式 <img alt="声学超晶格空腔" src="https:www.dealii.org/images/steps/developer/  step-62  .04.svg" height="200" //其中棕色代表材料_a，绿色代表材料_b。

    for (unsigned int idx = 0; idx < nb_mirror_pairs; idx++)
      {
        const double layer_transition_center =
          material_a_wavelength / 2 +
          idx * (material_b_wavelength / 4 + material_a_wavelength / 4);
        if (std::abs(p[0]) >=
              (layer_transition_center - average_rho_width / 2) &&
            std::abs(p[0]) <= (layer_transition_center + average_rho_width / 2))
          {
            const double coefficient =
              (std::abs(p[0]) -
               (layer_transition_center - average_rho_width / 2)) /
              average_rho_width;
            return (1 - coefficient) * material_a_rho +
                   coefficient * material_b_rho;
          }
      }

// 这里我们定义了[subpixel smoothing](https:meep.readthedocs.io/en/latest/Subpixel_Smoothing/)，它可以提高模拟的精度。

    for (unsigned int idx = 0; idx < nb_mirror_pairs; idx++)
      {
        const double layer_transition_center =
          material_a_wavelength / 2 +
          idx * (material_b_wavelength / 4 + material_a_wavelength / 4) +
          material_b_wavelength / 4;
        if (std::abs(p[0]) >=
              (layer_transition_center - average_rho_width / 2) &&
            std::abs(p[0]) <= (layer_transition_center + average_rho_width / 2))
          {
            const double coefficient =
              (std::abs(p[0]) -
               (layer_transition_center - average_rho_width / 2)) /
              average_rho_width;
            return (1 - coefficient) * material_b_rho +
                   coefficient * material_a_rho;
          }
      }

// 然后是腔体

    if (std::abs(p[0]) <= material_a_wavelength / 2)
      {
        return material_a_rho;
      }

// 材料层_a

    for (unsigned int idx = 0; idx < nb_mirror_pairs; idx++)
      {
        const double layer_center =
          material_a_wavelength / 2 +
          idx * (material_b_wavelength / 4 + material_a_wavelength / 4) +
          material_b_wavelength / 4 + material_a_wavelength / 8;
        const double layer_width = material_a_wavelength / 4;
        if (std::abs(p[0]) >= (layer_center - layer_width / 2) &&
            std::abs(p[0]) <= (layer_center + layer_width / 2))
          {
            return material_a_rho;
          }
      }

// material_b层

    for (unsigned int idx = 0; idx < nb_mirror_pairs; idx++)
      {
        const double layer_center =
          material_a_wavelength / 2 +
          idx * (material_b_wavelength / 4 + material_a_wavelength / 4) +
          material_b_wavelength / 8;
        const double layer_width = material_b_wavelength / 4;
        if (std::abs(p[0]) >= (layer_center - layer_width / 2) &&
            std::abs(p[0]) <= (layer_center + layer_width / 2))
          {
            return material_b_rho;
          }
      }

// 最后，默认的是 material_a。

    return material_a_rho;
  }

//  @sect4{The `Parameters` class implementation}

// 构造函数使用 HDF5::Group 函数从 HDF5::Group::get_attribute() `data`中读取所有参数。

  template <int dim>
  Parameters<dim>::Parameters(HDF5::Group &data)
    : data(data)
    , simulation_name(data.get_attribute<std::string>("simulation_name"))
    , save_vtu_files(data.get_attribute<bool>("save_vtu_files"))
    , start_frequency(data.get_attribute<double>("start_frequency"))
    , stop_frequency(data.get_attribute<double>("stop_frequency"))
    , nb_frequency_points(data.get_attribute<int>("nb_frequency_points"))
    , lambda(data.get_attribute<double>("lambda"))
    , mu(data.get_attribute<double>("mu"))
    , dimension_x(data.get_attribute<double>("dimension_x"))
    , dimension_y(data.get_attribute<double>("dimension_y"))
    , nb_probe_points(data.get_attribute<int>("nb_probe_points"))
    , grid_level(data.get_attribute<int>("grid_level"))
    , probe_start_point(data.get_attribute<double>("probe_pos_x"),
                        data.get_attribute<double>("probe_pos_y") -
                          data.get_attribute<double>("probe_width_y") / 2)
    , probe_stop_point(data.get_attribute<double>("probe_pos_x"),
                       data.get_attribute<double>("probe_pos_y") +
                         data.get_attribute<double>("probe_width_y") / 2)
    , right_hand_side(data)
    , pml(data)
    , rho(data)
  {}

//  @sect4{The `QuadratureCache` class implementation}

// 我们需要为质量和刚度矩阵以及右手边的矢量保留足够的空间。

  template <int dim>
  QuadratureCache<dim>::QuadratureCache(const unsigned int dofs_per_cell)
    : dofs_per_cell(dofs_per_cell)
    , mass_coefficient(dofs_per_cell, dofs_per_cell)
    , stiffness_coefficient(dofs_per_cell, dofs_per_cell)
    , right_hand_side(dofs_per_cell)
  {}

//  @sect3{Implementation of the `ElasticWave` class}
// @sect4{Constructor}

// 这与  step-40  的构造函数非常相似。此外，我们还创建了HDF5数据集`frequency_dataset`，`position_dataset`和`displacement`。注意在创建HDF5数据集时使用了 "模板 "关键字。这是C++的要求，使用`template`关键字是为了将`create_dataset`作为一个依赖的模板名称。

  template <int dim>
  ElasticWave<dim>::ElasticWave(const Parameters<dim> &parameters)
    : parameters(parameters)
    , mpi_communicator(MPI_COMM_WORLD)
    , triangulation(mpi_communicator,
                    typename Triangulation<dim>::MeshSmoothing(
                      Triangulation<dim>::smoothing_on_refinement |
                      Triangulation<dim>::smoothing_on_coarsening))
    , quadrature_formula(2)
    , fe(FE_Q<dim>(1), dim)
    , dof_handler(triangulation)
    , frequency(parameters.nb_frequency_points)
    , probe_positions(parameters.nb_probe_points, dim)
    , frequency_dataset(parameters.data.template create_dataset<double>(
        "frequency",
        std::vector<hsize_t>{parameters.nb_frequency_points}))
    , probe_positions_dataset(parameters.data.template create_dataset<double>(
        "position",
        std::vector<hsize_t>{parameters.nb_probe_points, dim}))
    , displacement(
        parameters.data.template create_dataset<std::complex<double>>(
          "displacement",
          std::vector<hsize_t>{parameters.nb_probe_points,
                               parameters.nb_frequency_points}))
    , pcout(std::cout,
            (Utilities::MPI::this_mpi_process(mpi_communicator) == 0))
    , computing_timer(mpi_communicator,
                      pcout,
                      TimerOutput::summary,
                      TimerOutput::wall_times)
  {}

//  @sect4{ElasticWave::setup_system}

// 这个函数没有什么新内容，与 step-40 的唯一区别是，我们不需要应用边界条件，因为我们使用PML来截断域。

  template <int dim>
  void ElasticWave<dim>::setup_system()
  {
    TimerOutput::Scope t(computing_timer, "setup");

    dof_handler.distribute_dofs(fe);

    locally_owned_dofs = dof_handler.locally_owned_dofs();
    DoFTools::extract_locally_relevant_dofs(dof_handler, locally_relevant_dofs);

    locally_relevant_solution.reinit(locally_owned_dofs,
                                     locally_relevant_dofs,
                                     mpi_communicator);

    system_rhs.reinit(locally_owned_dofs, mpi_communicator);

    constraints.clear();
    constraints.reinit(locally_relevant_dofs);
    DoFTools::make_hanging_node_constraints(dof_handler, constraints);

    constraints.close();

    DynamicSparsityPattern dsp(locally_relevant_dofs);

    DoFTools::make_sparsity_pattern(dof_handler, dsp, constraints, false);
    SparsityTools::distribute_sparsity_pattern(dsp,
                                               locally_owned_dofs,
                                               mpi_communicator,
                                               locally_relevant_dofs);

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

//  @sect4{ElasticWave::assemble_system}

// 这个函数也与 step-40 非常相似，尽管有明显的区别。我们为每个频率/欧米茄步骤组装系统。在第一步中，我们设置`calculate_quadrature_data = True`，然后我们计算质量和刚度矩阵以及右手边的矢量。在随后的步骤中，我们将使用这些数据来加速计算。

  template <int dim>
  void ElasticWave<dim>::assemble_system(const double omega,
                                         const bool   calculate_quadrature_data)
  {
    TimerOutput::Scope t(computing_timer, "assembly");

    FEValues<dim>      fe_values(fe,
                            quadrature_formula,
                            update_values | update_gradients |
                              update_quadrature_points | update_JxW_values);
    const unsigned int dofs_per_cell = fe.n_dofs_per_cell();
    const unsigned int n_q_points    = quadrature_formula.size();

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

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

// 这里我们存储右手边的值，rho和PML的值。

    std::vector<Vector<double>> rhs_values(n_q_points, Vector<double>(dim));
    std::vector<double>         rho_values(n_q_points);
    std::vector<Vector<std::complex<double>>> pml_values(
      n_q_points, Vector<std::complex<double>>(dim));

// 我们计算已经在jupyter笔记本中定义的 $\lambda$ 和 $\mu$ 的刚度张量。请注意，与 $\rho$ 相反，刚度在整个领域中是恒定的。

    const SymmetricTensor<4, dim> stiffness_tensor =
      get_stiffness_tensor<dim>(parameters.lambda, parameters.mu);

// 我们使用与 step-20 相同的方法处理矢量值问题。

    const FEValuesExtractors::Vector displacement(0);

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

// 只有当我们要计算质量和刚度矩阵时，我们才必须计算右手边的rho和PML的值。否则我们可以跳过这个计算，这样可以大大减少总的计算时间。

          if (calculate_quadrature_data)
            {
              fe_values.reinit(cell);

              parameters.right_hand_side.vector_value_list(
                fe_values.get_quadrature_points(), rhs_values);
              parameters.rho.value_list(fe_values.get_quadrature_points(),
                                        rho_values);
              parameters.pml.vector_value_list(
                fe_values.get_quadrature_points(), pml_values);
            }

// 我们已经在  step-18  中做了这个工作。获得一个指向当前单元本地正交缓存数据的指针，作为防御措施，确保这个指针在全局数组的范围内。

          QuadratureCache<dim> *local_quadrature_points_data =
            reinterpret_cast<QuadratureCache<dim> *>(cell->user_pointer());
          Assert(local_quadrature_points_data >= &quadrature_cache.front(),
                 ExcInternalError());
          Assert(local_quadrature_points_data <= &quadrature_cache.back(),
                 ExcInternalError());
          for (unsigned int q = 0; q < n_q_points; ++q)
            {

// quadrature_data变量用于存储质量和刚度矩阵、右手边向量和`JxW`的值。

              QuadratureCache<dim> &quadrature_data =
                local_quadrature_points_data[q];

// 下面我们声明力向量和PML的参数  $s$  和  $\xi$  。

              Tensor<1, dim>                       force;
              Tensor<1, dim, std::complex<double>> s;
              std::complex<double>                 xi(1, 0);

// 下面的块只在第一个频率步骤中计算。

              if (calculate_quadrature_data)
                {

// 存储`JxW`的值。

                  quadrature_data.JxW = fe_values.JxW(q);

                  for (unsigned int component = 0; component < dim; ++component)
                    {

// 将向量转换为张量，并计算出xi

                      force[component] = rhs_values[q][component];
                      s[component]     = pml_values[q][component];
                      xi *= s[component];
                    }

// 这里我们计算 $\alpha_{mnkl}$ 和 $\beta_{mnkl}$ 张量。

                  Tensor<4, dim, std::complex<double>> alpha;
                  Tensor<4, dim, std::complex<double>> beta;
                  for (unsigned int m = 0; m < dim; ++m)
                    for (unsigned int n = 0; n < dim; ++n)
                      for (unsigned int k = 0; k < dim; ++k)
                        for (unsigned int l = 0; l < dim; ++l)
                          {
                            alpha[m][n][k][l] = xi *
                                                stiffness_tensor[m][n][k][l] /
                                                (2.0 * s[n] * s[k]);
                            beta[m][n][k][l] = xi *
                                               stiffness_tensor[m][n][k][l] /
                                               (2.0 * s[n] * s[l]);
                          }

                  for (unsigned int i = 0; i < dofs_per_cell; ++i)
                    {
                      const Tensor<1, dim> phi_i =
                        fe_values[displacement].value(i, q);
                      const Tensor<2, dim> grad_phi_i =
                        fe_values[displacement].gradient(i, q);

                      for (unsigned int j = 0; j < dofs_per_cell; ++j)
                        {
                          const Tensor<1, dim> phi_j =
                            fe_values[displacement].value(j, q);
                          const Tensor<2, dim> grad_phi_j =
                            fe_values[displacement].gradient(j, q);

// 计算质量矩阵的值。

                          quadrature_data.mass_coefficient[i][j] =
                            rho_values[q] * xi * phi_i * phi_j;

//在刚度张量的 $mnkl$ 指数上循环。

                          std::complex<double> stiffness_coefficient = 0;
                          for (unsigned int m = 0; m < dim; ++m)
                            for (unsigned int n = 0; n < dim; ++n)
                              for (unsigned int k = 0; k < dim; ++k)
                                for (unsigned int l = 0; l < dim; ++l)
                                  {

// 这里我们计算刚度矩阵。
//注意，由于PML的存在，刚度矩阵不是对称的。我们使用梯度函数（见[文档](https:www.dealii.org/current/doxygen/deal.II/group__vector__valued.html)），它是一个  <code>Tensor@<2,dim@></code>  。
// 矩阵 $G_{ij}$ 由条目
                          // @f[
                          //  G_{ij}=
                          //  \frac{\partial\phi_i}{\partial x_j}
                          //  =\partial_j \phi_i
                          // @f]
                          // 组成 注意指数 $i$ 和 $j$ 的位置以及我们在本教程中使用的符号。  $\partial_j\phi_i$  . 由于刚度张量不是对称的，所以很容易出错。

                                    stiffness_coefficient +=
                                      grad_phi_i[m][n] *
                                      (alpha[m][n][k][l] * grad_phi_j[l][k] +
                                       beta[m][n][k][l] * grad_phi_j[k][l]);
                                  }

// 我们将刚度矩阵的值保存在quadrature_data中。

                          quadrature_data.stiffness_coefficient[i][j] =
                            stiffness_coefficient;
                        }

// 和正交数据中的右手边的值。

                        phi_i * force * fe_values.JxW(q);
                    }
                }

// 我们再次循环单元的自由度来计算系统矩阵。这些循环非常快，因为我们已经计算了刚度和质量矩阵，只有 $\omega$ 的值发生了变化。

              for (unsigned int i = 0; i < dofs_per_cell; ++i)
                {
                  for (unsigned int j = 0; j < dofs_per_cell; ++j)
                    {
                      std::complex<double> matrix_sum = 0;
                      matrix_sum += -std::pow(omega, 2) *
                                    quadrature_data.mass_coefficient[i][j];
                      matrix_sum += quadrature_data.stiffness_coefficient[i][j];
                      cell_matrix(i, j) += matrix_sum * quadrature_data.JxW;
                    }
                  cell_rhs(i) += quadrature_data.right_hand_side[i];
                }
            }
          cell->get_dof_indices(local_dof_indices);
          constraints.distribute_local_to_global(cell_matrix,
                                                 cell_rhs,
                                                 local_dof_indices,
                                                 system_matrix,
                                                 system_rhs);
        }

    system_matrix.compress(VectorOperation::add);
    system_rhs.compress(VectorOperation::add);
  }
// @sect4{ElasticWave::solve}

// 这比  step-40  更加简单。我们使用并行的直接求解器MUMPS，它比迭代求解器需要更少的选项。缺点是它不能很好地扩展。用迭代求解器来解决Helmholtz方程并不简单。移位拉普拉斯多网格法是一种众所周知的预处理该系统的方法，但这超出了本教程的范围。

  template <int dim>
  void ElasticWave<dim>::solve()
  {
    TimerOutput::Scope              t(computing_timer, "solve");
    LinearAlgebraPETSc::MPI::Vector completely_distributed_solution(
      locally_owned_dofs, mpi_communicator);

    SolverControl                    solver_control;
    PETScWrappers::SparseDirectMUMPS solver(solver_control, mpi_communicator);
    solver.solve(system_matrix, completely_distributed_solution, system_rhs);

    pcout << "   Solved in " << solver_control.last_step() << " iterations."
          << std::endl;
    constraints.distribute(completely_distributed_solution);
    locally_relevant_solution = completely_distributed_solution;
  }
// @sect4{ElasticWave::initialize_position_vector}

// 我们用这个函数来计算位置向量的值。

  template <int dim>
  void ElasticWave<dim>::initialize_probe_positions_vector()
  {
    for (unsigned int position_idx = 0;
         position_idx < parameters.nb_probe_points;
         ++position_idx)
      {

// 由于运算符+和

// -被重载来减去两个点，所以必须做如下操作。`Point_b<dim> + (-Point_a<dim>)`。

        const Point<dim> p =
          (position_idx / ((double)(parameters.nb_probe_points - 1))) *
            (parameters.probe_stop_point + (-parameters.probe_start_point)) +
          parameters.probe_start_point;
        probe_positions[position_idx][0] = p[0];
        probe_positions[position_idx][1] = p[1];
        if (dim == 3)
          {
            probe_positions[position_idx][2] = p[2];
          }
      }
  }
// @sect4{ElasticWave::store_frequency_step_data}

// 该函数在HDF5文件中存储探头测量的能量。

  template <int dim>
  void
  ElasticWave<dim>::store_frequency_step_data(const unsigned int frequency_idx)
  {
    TimerOutput::Scope t(computing_timer, "store_frequency_step_data");

// 我们存储 $x$ 方向的位移； $y$ 方向的位移可以忽略不计。

    const unsigned int probe_displacement_component = 0;

// 向量坐标包含HDF5文件中位于本地所有单元中的探测点的坐标。向量displacement_data包含这些点的位移值。

    std::vector<hsize_t>              coordinates;
    std::vector<std::complex<double>> displacement_data;

    const auto &mapping = get_default_linear_mapping(triangulation);
    GridTools::Cache<dim, dim> cache(triangulation, mapping);
    typename Triangulation<dim, dim>::active_cell_iterator cell_hint{};
    std::vector<bool>                                      marked_vertices = {};
    const double                                           tolerance = 1.e-10;

    for (unsigned int position_idx = 0;
         position_idx < parameters.nb_probe_points;
         ++position_idx)
      {
        Point<dim> point;
        for (unsigned int dim_idx = 0; dim_idx < dim; ++dim_idx)
          {
            point[dim_idx] = probe_positions[position_idx][dim_idx];
          }
        bool point_in_locally_owned_cell = false;
        {
          auto cell_and_ref_point = GridTools::find_active_cell_around_point(
            cache, point, cell_hint, marked_vertices, tolerance);
          if (cell_and_ref_point.first.state() == IteratorState::valid)
            {
              cell_hint = cell_and_ref_point.first;
              point_in_locally_owned_cell =
                cell_and_ref_point.first->is_locally_owned();
            }
        }
        if (point_in_locally_owned_cell)
          {

// 然后，我们可以在`displacement_data`中存储探头各点的位移值。

            Vector<std::complex<double>> tmp_vector(dim);
            VectorTools::point_value(dof_handler,
                                     locally_relevant_solution,
                                     point,
                                     tmp_vector);
            coordinates.emplace_back(position_idx);
            coordinates.emplace_back(frequency_idx);
            displacement_data.emplace_back(
              tmp_vector(probe_displacement_component));
          }
      }

// 我们在HDF5文件中写入位移数据。调用 HDF5::DataSet::write_selection() 是MPI集体的，这意味着所有进程都要参与。

    if (coordinates.size() > 0)
      {
        displacement.write_selection(displacement_data, coordinates);
      }

// 因此，即使进程没有数据可写，它也必须参与集体调用。为此我们可以使用  HDF5::DataSet::write_none().  注意，我们必须指定数据类型，在这种情况下  `std::complex<double>`.  。
    else
      {
        displacement.write_none<std::complex<double>>();
      }

// 如果输入文件中的变量`save_vtu_files`等于`True`，那么所有数据将被保存为vtu。写入`vtu'文件的过程已经在  step-40  中描述。

    if (parameters.save_vtu_files)
      {
        std::vector<std::string> solution_names(dim, "displacement");
        std::vector<DataComponentInterpretation::DataComponentInterpretation>
          interpretation(
            dim, DataComponentInterpretation::component_is_part_of_vector);

        DataOut<dim> data_out;
        data_out.add_data_vector(dof_handler,
                                 locally_relevant_solution,
                                 solution_names,
                                 interpretation);
        Vector<float> subdomain(triangulation.n_active_cells());
        for (unsigned int i = 0; i < subdomain.size(); ++i)
          subdomain(i) = triangulation.locally_owned_subdomain();
        data_out.add_data_vector(subdomain, "subdomain");

        std::vector<Vector<double>> force(
          dim, Vector<double>(triangulation.n_active_cells()));
        std::vector<Vector<double>> pml(
          dim, Vector<double>(triangulation.n_active_cells()));
        Vector<double> rho(triangulation.n_active_cells());

        for (auto &cell : triangulation.active_cell_iterators())
          {
            if (cell->is_locally_owned())
              {
                for (unsigned int dim_idx = 0; dim_idx < dim; ++dim_idx)
                  {
                    force[dim_idx](cell->active_cell_index()) =
                      parameters.right_hand_side.value(cell->center(), dim_idx);
                    pml[dim_idx](cell->active_cell_index()) =
                      parameters.pml.value(cell->center(), dim_idx).imag();
                  }
                rho(cell->active_cell_index()) =
                  parameters.rho.value(cell->center());
              }

// 在我们不感兴趣的单元格上，将各自的值设置为一个假值，以确保如果我们的假设有什么错误，我们会通过查看图形输出发现。

            else
              {
                for (unsigned int dim_idx = 0; dim_idx < dim; ++dim_idx)
                  {
                    force[dim_idx](cell->active_cell_index()) = -1e+20;
                    pml[dim_idx](cell->active_cell_index())   = -1e+20;
                  }
                rho(cell->active_cell_index()) = -1e+20;
              }
          }

        for (unsigned int dim_idx = 0; dim_idx < dim; ++dim_idx)
          {
            data_out.add_data_vector(force[dim_idx],
                                     "force_" + std::to_string(dim_idx));
            data_out.add_data_vector(pml[dim_idx],
                                     "pml_" + std::to_string(dim_idx));
          }
        data_out.add_data_vector(rho, "rho");

        data_out.build_patches();

        std::stringstream  frequency_idx_stream;
        const unsigned int nb_number_positions =
          ((unsigned int)std::log10(parameters.nb_frequency_points)) + 1;
        frequency_idx_stream << std::setw(nb_number_positions)
                             << std::setfill('0') << frequency_idx;
        std::string filename = (parameters.simulation_name + "_" +
                                frequency_idx_stream.str() + ".vtu");
        data_out.write_vtu_in_parallel(filename.c_str(), mpi_communicator);
      }
  }

//  @sect4{ElasticWave::output_results}

// 该函数写入尚未写入的数据集。

  template <int dim>
  void ElasticWave<dim>::output_results()
  {

// 向量`频率`和`位置`对所有进程都是一样的。因此任何一个进程都可以写入相应的`数据集'。因为调用 HDF5::DataSet::write 是MPI集体的，其余进程将不得不调用 HDF5::DataSet::write_none.  。
    if (Utilities::MPI::this_mpi_process(mpi_communicator) == 0)
      {
        frequency_dataset.write(frequency);
        probe_positions_dataset.write(probe_positions);
      }
    else
      {
        frequency_dataset.write_none<double>();
        probe_positions_dataset.write_none<double>();
      }
  }

//  @sect4{ElasticWave::setup_quadrature_cache}

// 我们在计算开始时使用这个函数来设置缓存变量的初始值。这个函数在  step-18  中已经描述过。与  step-18  的函数没有区别。

  template <int dim>
  void ElasticWave<dim>::setup_quadrature_cache()
  {
    triangulation.clear_user_data();

    {
      std::vector<QuadratureCache<dim>> tmp;
      quadrature_cache.swap(tmp);
    }

    quadrature_cache.resize(triangulation.n_locally_owned_active_cells() *
                              quadrature_formula.size(),
                            QuadratureCache<dim>(fe.n_dofs_per_cell()));
    unsigned int cache_index = 0;
    for (const auto &cell : triangulation.active_cell_iterators())
      if (cell->is_locally_owned())
        {
          cell->set_user_pointer(&quadrature_cache[cache_index]);
          cache_index += quadrature_formula.size();
        }
    Assert(cache_index == quadrature_cache.size(), ExcInternalError());
  }

//  @sect4{ElasticWave::frequency_sweep}

// 为了清楚起见，我们将 step-40 的函数`run`分为函数`run`和`frequency_sweep`。在函数`frequency_sweep`中，我们把迭代放在频率向量上。

  template <int dim>
  void ElasticWave<dim>::frequency_sweep()
  {
    for (unsigned int frequency_idx = 0;
         frequency_idx < parameters.nb_frequency_points;
         ++frequency_idx)
      {
        pcout << parameters.simulation_name + " frequency idx: "
              << frequency_idx << '/' << parameters.nb_frequency_points - 1
              << std::endl;

        setup_system();
        if (frequency_idx == 0)
          {
            pcout << "   Number of active cells :       "
                  << triangulation.n_active_cells() << std::endl;
            pcout << "   Number of degrees of freedom : "
                  << dof_handler.n_dofs() << std::endl;
          }

        if (frequency_idx == 0)
          {

// 只写一次模拟参数

            parameters.data.set_attribute("active_cells",
                                          triangulation.n_active_cells());
            parameters.data.set_attribute("degrees_of_freedom",
                                          dof_handler.n_dofs());
          }

// 我们计算出这个特定步骤的频率和欧米茄值。

        const double current_loop_frequency =
          (parameters.start_frequency +
           frequency_idx *
             (parameters.stop_frequency - parameters.start_frequency) /
             (parameters.nb_frequency_points - 1));
        const double current_loop_omega =
          2 * numbers::PI * current_loop_frequency;

// 在第一个频率步骤中，我们计算出质量和刚度矩阵以及右手边的数据。在随后的频率步骤中，我们将使用这些值。这大大改善了计算时间。

        assemble_system(current_loop_omega,
                        (frequency_idx == 0) ? true : false);
        solve();

        frequency[frequency_idx] = current_loop_frequency;
        store_frequency_step_data(frequency_idx);

        computing_timer.print_summary();
        computing_timer.reset();
        pcout << std::endl;
      }
  }

//  @sect4{ElasticWave::run}

// 这个函数与  step-40  中的函数非常相似。

  template <int dim>
  void ElasticWave<dim>::run()
  {
#ifdef DEBUG
    pcout << "Debug mode" << std::endl;
#else
    pcout << "Release mode" << std::endl;
#endif

    {
      Point<dim> p1;
      p1(0) = -parameters.dimension_x / 2;
      p1(1) = -parameters.dimension_y / 2;
      if (dim == 3)
        {
          p1(2) = -parameters.dimension_y / 2;
        }
      Point<dim> p2;
      p2(0) = parameters.dimension_x / 2;
      p2(1) = parameters.dimension_y / 2;
      if (dim == 3)
        {
          p2(2) = parameters.dimension_y / 2;
        }
      std::vector<unsigned int> divisions(dim);
      divisions[0] = int(parameters.dimension_x / parameters.dimension_y);
      divisions[1] = 1;
      if (dim == 3)
        {
          divisions[2] = 1;
        }
      GridGenerator::subdivided_hyper_rectangle(triangulation,
                                                divisions,
                                                p1,
                                                p2);
    }

    triangulation.refine_global(parameters.grid_level);

    setup_quadrature_cache();

    initialize_probe_positions_vector();

    frequency_sweep();

    output_results();
  }
} // namespace step62

//  @sect4{The main function}

// 主函数与  step-40  中的函数非常相似。

int main(int argc, char *argv[])
{
  try
    {
      using namespace dealii;
      const unsigned int dim = 2;

      Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv, 1);

      HDF5::File data_file("results.h5",
                           HDF5::File::FileAccessMode::create,
                           MPI_COMM_WORLD);
      auto       data = data_file.create_group("data");

// 每个模拟（位移和校准）都存储在一个单独的HDF5组中。

      const std::vector<std::string> group_names = {"displacement",
                                                    "calibration"};
      for (auto group_name : group_names)
        {

// 对于这两个组名中的每一个，我们现在创建组并将属性放入这些组。具体来说，这些是。

// - 波导的尺寸（在 $x$ 和 $y$ 方向）。

// - 探头的位置（在 $x$ 和 $y$ 方向）。

// - 探针中的点的数量

// - 全局细化水平

// - 腔体谐振频率

// - 镜像对的数量

// - 镜子的数量

// - 材料特性

// - 力的参数

// - PML参数

// - 频率参数

          auto group = data.create_group(group_name);

          group.set_attribute<double>("dimension_x", 2e-5);
          group.set_attribute<double>("dimension_y", 2e-8);
          group.set_attribute<double>("probe_pos_x", 8e-6);
          group.set_attribute<double>("probe_pos_y", 0);
          group.set_attribute<double>("probe_width_y", 2e-08);
          group.set_attribute<unsigned int>("nb_probe_points", 5);
          group.set_attribute<unsigned int>("grid_level", 1);
          group.set_attribute<double>("cavity_resonance_frequency", 20e9);
          group.set_attribute<unsigned int>("nb_mirror_pairs", 15);

          group.set_attribute<double>("poissons_ratio", 0.27);
          group.set_attribute<double>("youngs_modulus", 270000000000.0);
          group.set_attribute<double>("material_a_rho", 3200);

          if (group_name == std::string("displacement"))
            group.set_attribute<double>("material_b_rho", 2000);
          else
            group.set_attribute<double>("material_b_rho", 3200);

          group.set_attribute(
            "lambda",
            group.get_attribute<double>("youngs_modulus") *
              group.get_attribute<double>("poissons_ratio") /
              ((1 + group.get_attribute<double>("poissons_ratio")) *
               (1 - 2 * group.get_attribute<double>("poissons_ratio"))));
          group.set_attribute("mu",
                              group.get_attribute<double>("youngs_modulus") /
                                (2 * (1 + group.get_attribute<double>(
                                            "poissons_ratio"))));

          group.set_attribute<double>("max_force_amplitude", 1e26);
          group.set_attribute<double>("force_sigma_x", 1e-7);
          group.set_attribute<double>("force_sigma_y", 1);
          group.set_attribute<double>("max_force_width_x", 3e-7);
          group.set_attribute<double>("max_force_width_y", 2e-8);
          group.set_attribute<double>("force_x_pos", -8e-6);
          group.set_attribute<double>("force_y_pos", 0);

          group.set_attribute<bool>("pml_x", true);
          group.set_attribute<bool>("pml_y", false);
          group.set_attribute<double>("pml_width_x", 1.8e-6);
          group.set_attribute<double>("pml_width_y", 5e-7);
          group.set_attribute<double>("pml_coeff", 1.6);
          group.set_attribute<unsigned int>("pml_coeff_degree", 2);

          group.set_attribute<double>("center_frequency", 20e9);
          group.set_attribute<double>("frequency_range", 0.5e9);
          group.set_attribute<double>(
            "start_frequency",
            group.get_attribute<double>("center_frequency") -
              group.get_attribute<double>("frequency_range") / 2);
          group.set_attribute<double>(
            "stop_frequency",
            group.get_attribute<double>("center_frequency") +
              group.get_attribute<double>("frequency_range") / 2);
          group.set_attribute<unsigned int>("nb_frequency_points", 400);

          if (group_name == std::string("displacement"))
            group.set_attribute<std::string>(
              "simulation_name", std::string("phononic_cavity_displacement"));
          else
            group.set_attribute<std::string>(
              "simulation_name", std::string("phononic_cavity_calibration"));

          group.set_attribute<bool>("save_vtu_files", false);
        }

      {

// 位移模拟。参数从位移HDF5组中读取，结果保存在同一HDF5组中。

        auto                    displacement = data.open_group("displacement");
        step62::Parameters<dim> parameters(displacement);

        step62::ElasticWave<dim> elastic_problem(parameters);
        elastic_problem.run();
      }

      {

// 校准模拟。参数从校准HDF5组中读取，结果保存在同一HDF5组中。

        auto                    calibration = data.open_group("calibration");
        step62::Parameters<dim> parameters(calibration);

        step62::ElasticWave<dim> elastic_problem(parameters);
        elastic_problem.run();
      }
    }
  catch (std::exception &exc)
    {
      std::cerr << std::endl
                << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Exception on processing: " << std::endl
                << exc.what() << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;

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

  return 0;
}


/*
 DepSpawn: Data Dependent Spawn library
 Copyright (C) 2012-2021 Carlos H. Gonzalez, Basilio B. Fraguela. Universidade da Coruna

 Distributed under the MIT License. (See accompanying file LICENSE)
*/

///
/// \author   Carlos H. Gonzalez  <cgonzalezv@udc.es>
/// \author   Basilio B. Fraguela <basilio.fraguela@udc.es>
///

#include <cstdlib>
#include <iostream>
#include <chrono>
#include <blitz/array.h>
#include "depspawn/depspawn.h"
#include "common_io.cpp"  // This is only for serializing parallel prints

using namespace depspawn;
using namespace blitz;

typedef float Type;

#define N     2000
#define NTIMES  10

int CHUNKS;

Array<Type , 2> result(N,N), a(N, N), b(N,N);

/// @bug template<typename Type> //IT DOES NOT WORK WITH A TEMPLATED FUNCTION; AND IT SHOULD!
void add(Array<Type , 2>& result, const Array<Type , 2>& a, const Array<Type , 2>& b)
{
  const int nrows = result.rows();
  const int ncols = result.cols();

#ifdef SLOW

  for(int i = 0; i < nrows; i++) {
    for(int j = 0; j < ncols; j++) {
      result(i, j) += a(i, j)  + b(i, j);
    }
  }

#else

  const Type * const ap = a.data();
  const Type * const bp = b.data();
  Type * const rp = result.data();
  const int s = result.stride(firstDim);

  //LOG('(' << rp << " , " << s << " , " << ap[0] << ')');

  for(int i = 0; i < nrows; i++) {
    for(int j = 0; j < ncols; j++) {
      rp[i * s + j] += ap[i * s + j] + bp[i * s + j];
    }
  }

#endif

}

bool verify()
{
  const Type *ap = a.data();
  const Type *bp = b.data();
  const Type *rp = result.data();

  for (int i = 0; i < N; i++) {
    for(int j = 0; j < N; j++) {
      Type f = (ap[i * N + j] + bp[i * N + j]) * NTIMES;
      if (f != rp[i * N + j]) {
	std::cerr << "At (" << i << ", " << j << ") : " << rp[i * N + j] <<
	" != " << NTIMES << " *(" << ap[i * N + j] << " + " << bp[i * N + j] << ")\n";
	return false;
      }
    }
  }

  return true;
}

template<int extiters, int intiters>
void test()
{
  for (int iter = 0; iter < extiters; iter++) {
    for(int i = 0; i < N; i += N / CHUNKS) {
      int limi = (i + N / CHUNKS) >= N ? N : (i + N / CHUNKS);
      Range rows(i, limi - 1);
      for(int j = 0; j < N; j += N / CHUNKS) {
	int limj = (j + N / CHUNKS) >= N ? N : (j + N / CHUNKS);
	Range cols(j, limj - 1);
	for (int iter2 = 0; iter2 < intiters; iter2++)
	  spawn(add, result(rows, cols), a(rows, cols), b(rows, cols));
      }
    }
  }
}

void init_data()
{
  for (int i = 0; i < N; i++) {
    for(int j = 0; j < N; j++) {
      result(i, j) = (Type)0;
      a(i, j) = i + j;
      b(i, j) = (i > j) ? i - j : j - i;
    }
  }
}

int main(int argc, char **argv)
{

  CHUNKS = (argc == 1) ? 4 : atoi(argv[1]);

  init_data();

  std::chrono::time_point<std::chrono::high_resolution_clock> t0 = std::chrono::high_resolution_clock::now();

  for (int iter =0; iter < NTIMES; iter++) {
    add(result, a, b);
  }

  std::chrono::time_point<std::chrono::high_resolution_clock> t1 = std::chrono::high_resolution_clock::now();

  /************ First parallel run ************/

  init_data();

  std::chrono::time_point<std::chrono::high_resolution_clock> t2 = std::chrono::high_resolution_clock::now();

  test<NTIMES, 1>();

  wait_for_all();

  std::chrono::time_point<std::chrono::high_resolution_clock> t3 = std::chrono::high_resolution_clock::now();

  double serial_time = std::chrono::duration<double>(t1-t0).count();
  double parallel_time = std::chrono::duration<double>(t3-t2).count();

  std::cout << "Serial time: " << serial_time << "s.  Parallel time: " << parallel_time << "s.\n";
  std::cout << "Speedup (using " << CHUNKS << " X " << CHUNKS << " chunks): " <<  (serial_time / parallel_time) << std::endl;
  const bool test_ok1 = verify();
  std::cout << "TEST " << (test_ok1 ? "SUCCESSFUL" : "UNSUCCSESSFUL") << std::endl;

  /************ Second parallel run ************/

  init_data();

  t2 = std::chrono::high_resolution_clock::now();

  test<1, NTIMES>();

  wait_for_all();

  t3 = std::chrono::high_resolution_clock::now();

  parallel_time = std::chrono::duration<double>(t3-t2).count();

  std::cout << "Serial time: " << serial_time << "s.  Parallel time: " << parallel_time << "s.\n";
  std::cout << "Speedup (using " << CHUNKS << " X " << CHUNKS << " chunks): " <<  (serial_time / parallel_time) << std::endl;
  const bool test_ok2 = verify();
  std::cout << "TEST " << (test_ok2 ? "SUCCESSFUL" : "UNSUCCSESSFUL") << std::endl;

  return !(test_ok1 && test_ok2);
}


/**
 *  This test ensures that the sketch application (for Elemental matrices) is
 *  done correctly (on-the-fly matrix multiplication in the code is compared
 *  to true matrix multiplication).
 *  This test builds on the following assumptions:
 *
 *      - El::Gemm returns the correct result, and
 *      - the random numbers in row_idx and row_value (see
 *        hash_transform_data_t) are drawn from the promised distributions.
 */

#include <vector>

#include <El.hpp>

#define SKYLARK_NO_ANY
#include "../../utility/distributions.hpp"
#include "../../sketch/sketch.hpp"

#include <boost/mpi.hpp>
#include <boost/test/minimal.hpp>

template < typename InputMatrixType,
           typename OutputMatrixType = InputMatrixType >
struct Dummy_t : public skylark::sketch::hash_transform_t<
    InputMatrixType, OutputMatrixType,
    boost::random::uniform_int_distribution,
    skylark::utility::rademacher_distribution_t > {

    typedef skylark::sketch::hash_transform_t<
        InputMatrixType, OutputMatrixType,
        boost::random::uniform_int_distribution,
        skylark::utility::rademacher_distribution_t >
            hash_t;

    Dummy_t(int N, int S, skylark::base::context_t& context)
        : skylark::sketch::hash_transform_t<InputMatrixType, OutputMatrixType,
          boost::random::uniform_int_distribution,
          skylark::utility::rademacher_distribution_t>(N, S, context)
    {}

    std::vector<size_t> getRowIdx() { return hash_t::row_idx; }
    std::vector<double> getRowValues() { return hash_t::row_value; }
};

int test_main(int argc, char *argv[]) {

    //////////////////////////////////////////////////////////////////////////
    //[> Parameters <]

    //FIXME: use random sizes?
    const size_t n   = 10;
    const size_t m   = 5;
    const size_t n_s = 6;
    const size_t m_s = 3;

    //////////////////////////////////////////////////////////////////////////
    //[> Setup test <]
    namespace mpi = boost::mpi;

    El::Initialize(argc, argv);

    mpi::environment env(argc, argv);
    mpi::communicator world;

    MPI_Comm mpi_world(world);
    El::Grid grid(mpi_world);

    typedef El::DistMatrix<double, El::CIRC, El::CIRC> MatrixType;
    typedef El::DistMatrix<double, El::VR, El::STAR> DistMatrixType;

    skylark::base::context_t context (0);

    double count = 1.0;
    El::DistMatrix<double, El::VR, El::STAR> A(grid);
    El::Uniform (A, n, m);
    for( size_t j = 0; j < A.Height(); j++ )
        for( size_t i = 0; i < A.Width(); i++ )
            A.Set(j, i, count++);

    //////////////////////////////////////////////////////////////////////////
    //[> Column wise application <]

    /* 1. Create the sketching matrix */
    Dummy_t<DistMatrixType, MatrixType> Sparse(n, n_s, context);
    std::vector<size_t> row_idx    = Sparse.getRowIdx();
    std::vector<double> row_val = Sparse.getRowValues();

    // PI generated by random number gen
    El::DistMatrix<double, El::VR, El::STAR> pi_sketch(grid);
    El::Uniform(pi_sketch, n_s, n);
    El::Zero(pi_sketch);
    for(size_t i = 0; i < row_idx.size(); ++i)
        pi_sketch.Set(row_idx[i], i, row_val[i]);

    /* 2. Create space for the sketched matrix */
    MatrixType sketch_A(n_s, m);

    /* 3. Apply the transform */
    Sparse.apply(A, sketch_A, skylark::sketch::columnwise_tag());

    /* 4. Build structure to compare */
    El::DistMatrix<double, El::VR, El::STAR> expected_A(grid);
    El::Uniform (expected_A, n_s, m);
    El::Gemm(El::NORMAL, El::NORMAL,
               1.0, pi_sketch.LockedMatrix(), A.LockedMatrix(),
               0.0, expected_A.Matrix());

    for(size_t j = 0; j < sketch_A.Height(); j++ )
        for(size_t i = 0; i < sketch_A.Width(); i++ )
            if(sketch_A.Get(j, i) != expected_A.Get(j,i)) {
                std::cerr << sketch_A.Get(j, i)  << " != "
                          << expected_A.Get(j, i) << std::endl;
                BOOST_FAIL("Result of colwise application not as expected");
            }

    //////////////////////////////////////////////////////////////////////////
    //[> Row wise application <]

    //[> 1. Create the sketching matrix <]
    Dummy_t<DistMatrixType, MatrixType> Sparse_r (m, m_s, context);
    row_idx.clear(); row_val.clear();
    row_idx = Sparse_r.getRowIdx();
    row_val = Sparse_r.getRowValues();

    // PI^T generated by random number gen
    El::DistMatrix<double, El::VR, El::STAR> pi_sketch_r(grid);
    El::Uniform(pi_sketch_r, m, m_s);
    El::Zero(pi_sketch_r);
    for(size_t i = 0; i < row_idx.size(); ++i)
        pi_sketch_r.Set(i, row_idx[i], row_val[i]);

    //[> 2. Create space for the sketched matrix <]
    MatrixType sketch_A_r(n, m_s);

    //[> 3. Apply the transform <]
    Sparse_r.apply (A, sketch_A_r, skylark::sketch::rowwise_tag());

    /* 4. Build structure to compare */
    El::DistMatrix<double, El::VR, El::STAR> expected_AR(grid);
    El::Uniform (expected_AR, n, m_s);
    El::Gemm(El::NORMAL, El::NORMAL,
               1.0, A.LockedMatrix(), pi_sketch_r.Matrix(),
               0.0, expected_AR.Matrix());

    for(size_t j = 0; j < sketch_A_r.Height(); j++ )
        for(size_t i = 0; i < sketch_A_r.Width(); i++ )
            if(sketch_A_r.Get(j, i) != expected_AR.Get(j,i)) {
                std::cerr << sketch_A_r.Get(j, i)  << " != "
                          << expected_AR.Get(j, i) << std::endl;
                BOOST_FAIL("Result of rowwise application not as expected");
            }

    El::Finalize();
    return 0;
}


#include <boost/random/linear_feedback_shift.hpp>


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

#pragma once

//stdlib
#include <iostream>

//libraries
#include <Eigen/Eigen>

//local
#include "../math/typedefs.hpp"

namespace eig = Eigen;

namespace telemetry {

/**
 * A structure for logging statistics pertaining to warps at the end of an optimization iteration
 * (in warp- or warp-update-threshold-based optimizations)
 */
template<typename Coordinates>
struct WarpDeltaStatistics {

	float ratio_above_min_threshold = 0.0;
	float length_min = 0.0f;
	float length_max = 0.0f;
	float length_mean = 0.0;
	float length_standard_deviation = 0.0;
	Coordinates longest_warp_location = Coordinates(0);
	bool is_largest_below_min_threshold = false;
	bool is_largest_above_max_threshold = false;

	WarpDeltaStatistics() = default;
	WarpDeltaStatistics(
			float ratio_above_min_threshold,
			float length_min,
			float length_max,
			float length_mean,
			float length_standard_deviation,
			Coordinates longest_warp_location,
			bool is_largest_below_min_threshold,
			bool is_largest_above_max_threshold
			);

	eig::VectorXf to_array();

	bool operator==(const WarpDeltaStatistics& rhs);
	bool operator!=(const WarpDeltaStatistics& rhs);
};

template<typename Coordinates, typename ScalarContainer, typename VectorContainer>
WarpDeltaStatistics<Coordinates> build_warp_delta_statistics(const VectorContainer& warp_field,
		const ScalarContainer& canonical_field,
		const ScalarContainer& live_field,
		float min_threshold, float max_threshold);

template<typename Coordinates>
std::ostream &operator<<(std::ostream &ostr, const WarpDeltaStatistics<Coordinates> &ts);

typedef WarpDeltaStatistics<math::Vector2i> WarpDeltaStatistics2d;
typedef WarpDeltaStatistics<math::Vector3i> WarpDeltaStatistics3d;

} //namespace telemetry


//    X = STR2DOUBLEQ(S) converts the string S, which should be an
//    ASCII character representation of a real value, to MATLAB's double
//    representation.  The string may contain digits,a decimal point,
//    a leading + or - sign and 'e' preceding a power of 10 scale factor
//
//    X = STR2DOUBLEQ(C) converts the strings in the cell array of strings
//    C to double.  The matrix X returned will be the same size as C.
//    NaN will be returned for any cell which is not a string representing
//    a valid scalar value. NaN will be returned for individual cells in
//    C which are cell arrays.

//    Examples
//       str2doubleq('3.14159')
//	     str2doubleq('  2 i   +   4.23e2  ')
//		 str2doubleq('  2 +  i4     ')
//       str2doubleq({'2e10' '3.1415'})
//       str2doubleq(' j4     ')
//		 str2doubleq('100+ 10E2i')
//		 str2doubleq('1.25e10')

#include "mex.h"
#include <cmath>
/* Please uncomment the following #define directive line
   USE_PARALLEL_ALGORITHM if you wish to compile algorithm
   in parallel computation mode. If this is enabled either
   you need to have
		- boost library installed (please see http://www.boost.org/
		  instructions to build boost library or if using Visual Studio,
		  just download precompiled binaries from http://www.boostpro.com/download/
		  using latest installer)
		- compiler that supports C++11 native threads

*/
//#define USE_PARALLEL_ALGORITHM

/* Please uncomment the following #define directive
   ENSURE_THREAD_SAFE_MEX if you are using USE_PARALLEL_ALGORITHM
   and you are experiencing Matlab crash during calls to str2doubleq.
   Atleast with version 2012b Matlab does not crash with simultaneous
   threads calling functions in mex.h
   The following macro compiles the source ensuring thread safeness in
   Matlab runtime. Also note that if ENSURE_THREAD_SAFE_MEX is undefined
   algorithm is considerably more faster!
*/
//#define ENSURE_THREAD_SAFE_MEX

static const double NaN =  mxGetNaN();

inline bool is_null_or_blank(const char*& s) {
	if (!s)
		return true;
	while (*s)
		if (!(*s == '\t' || *s == ' '))
			return false;
		else
			++s;
	return true;
}

inline void remove_blanks(const char*& s) {
	while (*s == '\t' || *s == ' ')
		s++;
}

bool parse_to_double(const char*& s , double& dval, bool& is_imaginary) {
	dval = 0;
	is_imaginary = false;
	if (!s)
		return false;
	remove_blanks(s);
	bool is_negative = false;
	switch (*s) {
		case '-':
			is_negative = true;
		case '+':
			++s;
	}
	remove_blanks(s);
	if (*s == 'i' || *s == 'j') {
		++s;
		is_imaginary = true;
	}
	remove_blanks(s);
	/* skip trailing zeros*/
	while (*s == '0')
		s++;
	/* whole part is processed in doubles to avoid overflows */
	while (*s >= '0' && *s <= '9') {
		dval *= 10;
		dval += (double) (*s++ - '0');
	}
	if (*s == '.' || *s == ',') {
		s++;
		/* decimal part */
		double decimal = 0;
		double divisor = 1;
		while (*s >= '0' && *s <= '9') {
			divisor *= 10;
			decimal *= 10;
			decimal += (double) (*s++ - '0');
		}
		dval += decimal / divisor;
	}
	if(*s == 'e' || *s == 'E') {
		s++;
		/* scientific notation */
		bool is_negative_exp = false;
		int exp = 0;
		int exp_count = 0;
		switch (*s) {
			case '-':
				is_negative_exp = true;
			case '+':
				s++;
		}
		while (*s >= '0' && *s <= '9') {
			exp *= 10;
			exp += (int) (*s++ - '0');
			exp_count++;
		}
		if (exp_count == 0)
			return false;
		else if (is_negative_exp)
			exp = -1 * exp;
		dval *= pow(10.0,exp);
	}
	if (is_negative)
		dval *= -1;
	remove_blanks(s);
	if (*s == 'i' || *s == 'j')
		if (is_imaginary)
			return false;
		else {
			is_imaginary = true;
			++s;
		}
	remove_blanks(s);
	return true;
}

bool string_to_double(const char*& s , double& real, double& imag)
{
	real = 0; imag = 0;
	double d = 0;
	bool is_imag_1 = false; bool is_imag_2 = false;

	if (parse_to_double(s,d,is_imag_1)) {
		if (is_imag_1)
			imag = d;
		else
			real = d;
		if (is_null_or_blank(s))
			return true;
		else if (parse_to_double(s,d,is_imag_2)) {
			if (is_imag_1 && is_imag_2)
				return false;
			else if (is_imag_2)
				imag = d;
			else
				real = d;
			return is_null_or_blank(s);
		}
		else
			return false;
	}
	else
		return false;
}

#ifdef USE_PARALLEL_ALGORITHM
// following includes if using boost
#include <vector>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/bind.hpp>
/* Following includes if using native std with C++11 features.
   Please note that the set of compilers supporting new C++ standard
   features is (in the time of writing 09 Oct 2012) very scarce.
   So if your mex command returns compiler errors like missing
   <thread> library, most propably your compiler does not yet
   support the new C++11 standard.
*/
//#include <functional>
//#include <thread>

static const unsigned n_threads = boost::thread::hardware_concurrency();
//static const unsigned n_threads = std::thread::hardware_concurrency();

namespace ThreadSafeMex {
	/* Matlab runtime is not thread safe by its own. Only a single
	   thread of execution can interract with Matlab runtime in a safe
	   fashion. Otherwise the behaviour is undefined.
	   by ow
	*/
#ifdef ENSURE_THREAD_SAFE_MEX
	boost::mutex m;
	//std::mutex m;
#endif
	inline mxArray *mxGetCell(const mxArray *pm, mwIndex index) {
#ifdef ENSURE_THREAD_SAFE_MEX
		//std::lock(m);
		boost::mutex::scoped_lock lock(m);
#endif
		return ::mxGetCell(pm,index);
	}
	inline char *mxArrayToString(const mxArray *array_ptr)
	{
#ifdef ENSURE_THREAD_SAFE_MEX
		//std::lock(m);
		boost::mutex::scoped_lock lock(m);
#endif
		return ::mxArrayToString(array_ptr);
	}
	inline bool mxIsChar(const mxArray *pm)
	{
#ifdef ENSURE_THREAD_SAFE_MEX
		//std::lock(m);
		boost::mutex::scoped_lock lock(m);
#endif
		return ::mxIsChar(pm);
	}
};

void parallel_str_to_double_task(const mxArray* mxStr,
								 double* real, double* imag,
								 size_t job_id_start, size_t job_id_end)
{
	mxArray *cell = 0;
	const char *s = 0;
	for (size_t i = job_id_start; i < job_id_end; i++) {
		cell = ThreadSafeMex::mxGetCell(mxStr,i);
		if (cell == 0 || !ThreadSafeMex::mxIsChar(cell)) {
			real[i] = NaN;
			imag[i] = 0;
		}
		else {
			s = ThreadSafeMex::mxArrayToString(cell);
			if(!string_to_double(s,real[i],imag[i])) {
				real[i] = NaN;
				imag[i] = 0;
			}
		}
	}
}
#endif

void mexFunction( int nlhs, mxArray *plhs[],
		  int nrhs, const mxArray*prhs[] )

{
	const mxArray *mxStr = prhs[0];
	if (nrhs == 0)
		mexErrMsgTxt("Too few input arguments");
	else if  (nrhs > 1)
		mexErrMsgTxt("Too many input arguments.");
	if (mxIsChar(mxStr)) {
		plhs[0] = mxCreateDoubleMatrix(1,1, mxCOMPLEX);
		const char *s = mxArrayToString(mxStr);
		double *real = mxGetPr(plhs[0]);
		double *imag = mxGetPi(plhs[0]);
		if (!string_to_double(s,
							  real[0],
							  imag[0])) {
			real[0] = NaN;
			imag[0] = 0;
		}
	}
	else if (mxIsCell(mxStr)) {
		size_t n = mxGetNumberOfElements(mxStr);
		plhs[0] = mxCreateNumericArray(mxGetNumberOfDimensions(mxStr),
									   mxGetDimensions(mxStr),
									   mxDOUBLE_CLASS,
									   mxCOMPLEX);
		double *real = mxGetPr(plhs[0]);
		double *imag = mxGetPi(plhs[0]);

#ifdef USE_PARALLEL_ALGORITHM
		//using std::thread;
		//using std::bind;
		using boost::thread;
		using boost::bind;

		if (n < 1024 || n_threads <= 1) {
			// too small task to do in parallel. Just serialize
			mxArray *cell = 0; const char *s = 0;
			for (size_t i = 0; i < n; i++) {
				cell = mxGetCell(mxStr,i);
				if (cell == 0 || !mxIsChar(cell)) {
					real[i] = NaN;
					imag[i] = 0;
				}
				else {
					s = mxArrayToString(cell);
					if(!string_to_double(s,real[i],imag[i])) {
						real[i] = NaN;
						imag[i] = 0;
					}
				}
			}
			return;
		}

		const size_t grain_size = (size_t)((double)n / n_threads);
		std::vector<thread*> threads(n_threads,0);

		size_t job_first = 0;
		for(size_t i = 0; i < n_threads; ++i) {
			threads[i] = new thread(bind(parallel_str_to_double_task,
										 mxStr,
										 real,
										 imag,
										 job_first,
										 i < n_threads -1 ? job_first + grain_size : n));
			job_first += grain_size;
		}

		// main thread is blocked in the following loop until all works finished
		for(size_t i = 0; i < threads.size(); ++i) {
			threads[i]->join();
			delete threads[i];
		}
#else
		mxArray *cell = 0; const char *s = 0;
		for (size_t i = 0; i < n; i++) {
			cell = mxGetCell(mxStr,i);
			if (cell == 0 || !mxIsChar(cell)) {
				real[i] = NaN;
				imag[i] = 0;
			}
			else {
				s = mxArrayToString(cell);
				if(!string_to_double(s,real[i],imag[i])) {
					real[i] = NaN;
					imag[i] = 0;
				}
			}
		}
#endif
	}
	else if (mxIsDouble(mxStr)) {
		if (mxIsEmpty(mxStr)) {
			plhs[0] = mxCreateDoubleScalar(NaN);
			return;
		}
		// return vector of NaN's
		size_t n = mxGetNumberOfElements(mxStr);
		plhs[0] = mxCreateNumericArray(mxGetNumberOfDimensions(mxStr),
									   mxGetDimensions(mxStr),
									   mxDOUBLE_CLASS,
									   mxREAL);
		double *d = mxGetPr(plhs[0]);
		for (size_t i = 0; i < n; i++)
			d[i] = NaN;
	}
	else {
		// case to handle other situations, eg input is a class etc....
		plhs[0] = mxCreateDoubleScalar(NaN);
	}
};


// Copyright John Maddock 2006.
// Copyright Paul A. Bristow 2007, 2009
//  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)

#define BOOST_MATH_OVERFLOW_ERROR_POLICY ignore_error

#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
#include <boost/test/tools/floating_point_comparison.hpp>
#include <boost/math/tools/stats.hpp>
#include <boost/math/tools/test.hpp>
#include <boost/math/constants/constants.hpp>
#include <boost/math/special_functions/gamma.hpp>
#include <boost/multiprecision/cpp_bin_float.hpp>
#include <boost/array.hpp>
#include "functor.hpp"

#include "handle_test_result.hpp"
#include "table_type.hpp"

#ifndef SC_
#define SC_(x) static_cast<typename table_type<T>::type>(BOOST_STRINGIZE(x))
#endif

template <class Real, class T>
void do_test_gamma(const T& data, const char* type_name, const char* test_name)
{
#if !(defined(ERROR_REPORTING_MODE) && (!defined(TGAMMA_FUNCTION_TO_TEST) || !defined(LGAMMA_FUNCTION_TO_TEST)))
   typedef Real                   value_type;

   typedef value_type (*pg)(value_type);
#ifdef TGAMMA_FUNCTION_TO_TEST
   pg funcp = TGAMMA_FUNCTION_TO_TEST;
#elif defined(BOOST_MATH_NO_DEDUCED_FUNCTION_POINTERS)
   pg funcp = boost::math::tgamma<value_type>;
#else
   pg funcp = boost::math::tgamma;
#endif

   boost::math::tools::test_result<value_type> result;

   std::cout << "Testing " << test_name << " with type " << type_name
      << "\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";

   //
   // test tgamma against data:
   //
   result = boost::math::tools::test_hetero<Real>(
      data,
      bind_func<Real>(funcp, 0),
      extract_result<Real>(1));
   handle_test_result(result, data[result.worst()], result.worst(), type_name, "tgamma", test_name);
   //
   // test lgamma against data:
   //
#ifdef LGAMMA_FUNCTION_TO_TEST
   funcp = LGAMMA_FUNCTION_TO_TEST;
#elif defined(BOOST_MATH_NO_DEDUCED_FUNCTION_POINTERS)
   funcp = boost::math::lgamma<value_type>;
#else
   funcp = boost::math::lgamma;
#endif
   result = boost::math::tools::test_hetero<Real>(
      data,
      bind_func<Real>(funcp, 0),
      extract_result<Real>(2));
   handle_test_result(result, data[result.worst()], result.worst(), type_name, "lgamma", test_name);

   std::cout << std::endl;
#endif
}

template <class T>
void test_gamma(T, const char* name)
{
   //
   // The actual test data is rather verbose, so it's in a separate file
   //
   // The contents are as follows, each row of data contains
   // three items, input value, gamma and lgamma:
   //
   // gamma and lgamma at integer and half integer values:
   // boost::array<boost::array<T, 3>, N> factorials;
   //
   // gamma and lgamma for z near 0:
   // boost::array<boost::array<T, 3>, N> near_0;
   //
   // gamma and lgamma for z near 1:
   // boost::array<boost::array<T, 3>, N> near_1;
   //
   // gamma and lgamma for z near 2:
   // boost::array<boost::array<T, 3>, N> near_2;
   //
   // gamma and lgamma for z near -10:
   // boost::array<boost::array<T, 3>, N> near_m10;
   //
   // gamma and lgamma for z near -55:
   // boost::array<boost::array<T, 3>, N> near_m55;
   //
   // The last two cases are chosen more or less at random,
   // except that one is even and the other odd, and both are
   // at negative poles.  The data near zero also tests near
   // a pole, the data near 1 and 2 are to probe lgamma as
   // the result -> 0.
   //
#  include "tgamma_mp_data.hpp"

   do_test_gamma<T>(factorials, name, "factorials");
   do_test_gamma<T>(near_0, name, "near 0");
   do_test_gamma<T>(near_1, name, "near 1");
   do_test_gamma<T>(near_2, name, "near 2");
   do_test_gamma<T>(near_m10, name, "near -10");
   do_test_gamma<T>(near_m55, name, "near -55");
}

void expected_results()
{
   //
   // Define the max and mean errors expected for
   // various compilers and platforms.
   //
   add_expected_result(
      ".*",                          // compiler
      ".*",                          // stdlib
      ".*",                          // platform
      "number<cpp_bin_float<65> >",           // test type(s)
      ".*",                          // test data group
      "lgamma", 9000, 4000);      // test function
   add_expected_result(
      ".*",                          // compiler
      ".*",                          // stdlib
      ".*",                          // platform
      "number<cpp_bin_float<75> >",           // test type(s)
      ".*",                          // test data group
      "lgamma", 60000, 20000);      // test function
   add_expected_result(
      ".*",                          // compiler
      ".*",                          // stdlib
      ".*",                          // platform
      "cpp_bin_float_100|number<cpp_bin_float<85> >",           // test type(s)
      ".*",                          // test data group
      "lgamma", 600000, 300000);      // test function
   add_expected_result(
      ".*",                          // compiler
      ".*",                          // stdlib
      ".*",                          // platform
      ".*",                          // test type(s)
      ".*",                          // test data group
      "lgamma", 4800, 2500);           // test function
   add_expected_result(
      ".*",                          // compiler
      ".*",                          // stdlib
      ".*",                          // platform
      ".*",                          // test type(s)
      ".*",                          // test data group
      "[tl]gamma", 100, 50);            // test function
   //
   // Finish off by printing out the compiler/stdlib/platform names,
   // we do this to make it easier to mark up expected error rates.
   //
   std::cout << "Tests run with " << BOOST_COMPILER << ", "
      << BOOST_STDLIB << ", " << BOOST_PLATFORM << std::endl;
}

BOOST_AUTO_TEST_CASE(test_main)
{
   expected_results();
   using namespace boost::multiprecision;
   test_gamma(number<cpp_bin_float<38> >(0), "number<cpp_bin_float<38> >");
   test_gamma(number<cpp_bin_float<45> >(0), "number<cpp_bin_float<45> >");
   test_gamma(cpp_bin_float_50(0), "cpp_bin_float_50");
   test_gamma(number<cpp_bin_float<55> >(0), "number<cpp_bin_float<55> >");
   test_gamma(number<cpp_bin_float<65> >(0), "number<cpp_bin_float<65> >");
   test_gamma(number<cpp_bin_float<75> >(0), "number<cpp_bin_float<75> >");
   test_gamma(number<cpp_bin_float<85> >(0), "number<cpp_bin_float<85> >");
   test_gamma(cpp_bin_float_100(0), "cpp_bin_float_100");
}


//==================================================================================================
/*!

  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)
*/
//==================================================================================================
#include <boost/simd/function/round.hpp>
#include <scalar_test.hpp>
#include <boost/simd/detail/dispatch/meta/as_integer.hpp>
#include <boost/simd/constant/inf.hpp>
#include <boost/simd/constant/minf.hpp>
#include <boost/simd/constant/mone.hpp>
#include <boost/simd/constant/nan.hpp>
#include <boost/simd/constant/one.hpp>
#include <boost/simd/constant/zero.hpp>
#include <boost/simd/constant/half.hpp>
#include <boost/simd/constant/mhalf.hpp>
#include <boost/simd/detail/constant/maxflint.hpp>
#include <boost/simd/function/next.hpp>
#include <boost/simd/function/prev.hpp>

STF_CASE_TPL ( "round std",  STF_IEEE_TYPES)
{
  namespace bs = boost::simd;
  namespace bd = boost::dispatch;
  using bs::round;
  using r_t = decltype(bs::std_(round)(T()));

  // return type conformity test
  STF_TYPE_IS( r_t, T );

  // specific values tests
  STF_IEEE_EQUAL(bs::std_(round)(T(-1.4)), -1);
  STF_IEEE_EQUAL(bs::std_(round)(T(-1.5)), -2);
  STF_IEEE_EQUAL(bs::std_(round)(T(-1.6)), -2);
  STF_IEEE_EQUAL(bs::std_(round)(T(-2.5)), -3);
   STF_IEEE_EQUAL(bs::std_(round)(T(1.4)), 1);
  STF_IEEE_EQUAL(bs::std_(round)(T(1.5)), 2);
  STF_IEEE_EQUAL(bs::std_(round)(T(1.6)), 2);
  STF_IEEE_EQUAL(bs::std_(round)(T(2.5)), 3);
  STF_IEEE_EQUAL(bs::std_(round)(bs::Half<T>()), bs::One<r_t>());
  STF_IEEE_EQUAL(bs::std_(round)(bs::Inf<T>()), bs::Inf<r_t>());
  STF_IEEE_EQUAL(bs::std_(round)(bs::Mhalf<T>()), bs::Mone<r_t>());
  STF_IEEE_EQUAL(bs::std_(round)(bs::Minf<T>()), bs::Minf<r_t>());
  STF_IEEE_EQUAL(bs::std_(round)(bs::Mone<T>()), bs::Mone<r_t>());
  STF_IEEE_EQUAL(bs::std_(round)(bs::Nan<T>()), bs::Nan<r_t>());
  STF_IEEE_EQUAL(bs::std_(round)(bs::One<T>()), bs::One<r_t>());
  STF_IEEE_EQUAL(bs::std_(round)(bs::Zero<T>()), bs::Zero<r_t>());
} // end of test for floating_


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

// Copyright (c) 2010-2011 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)

#ifndef BOOST_GEOMETRY_TEST_DISTANCE_HPP
#define BOOST_GEOMETRY_TEST_DISTANCE_HPP

#include <geometry_test_common.hpp>

#include <boost/geometry/algorithms/distance.hpp>
#include <boost/geometry/domains/gis/io/wkt/read_wkt.hpp>
#include <boost/geometry/strategies/strategies.hpp>

// Define a custom distance strategy
// For this one, the "taxicab" distance,
// see http://en.wikipedia.org/wiki/Taxicab_geometry

// For a point-point-distance operation, one typename Point is enough.
// For a point-segment-distance operation, there is some magic inside
// using another point type and casting if necessary. Therefore,
// two point-types are necessary.
template <typename P1, typename P2 = P1>
struct taxicab_distance
{
    static inline typename bg::coordinate_type<P1>::type apply(
                    P1 const& p1, P2 const& p2)
    {
        using bg::get;
        using bg::math::abs;
        return abs(get<0>(p1) - get<1>(p2))
            + abs(get<1>(p1) - get<1>(p2));
    }
};

namespace boost { namespace geometry { namespace strategy { namespace distance { namespace services
{

template <typename P1, typename P2>
struct tag<taxicab_distance<P1, P2> >
{
    typedef strategy_tag_distance_point_point type;
};

template <typename P1, typename P2>
struct return_type<taxicab_distance<P1, P2> >
{
    typedef typename coordinate_type<P1>::type type;
};

template<typename P1, typename P2, typename PN1, typename PN2>
struct similar_type<taxicab_distance<P1, P2>, PN1, PN2>
{
    typedef taxicab_distance<PN1, PN2> type;
};

template<typename P1, typename P2, typename PN1, typename PN2>
struct get_similar<taxicab_distance<P1, P2>, PN1, PN2>
{
    static inline typename similar_type
        <
            taxicab_distance<P1, P2>, PN1, PN2
        >::type apply(taxicab_distance<P1, P2> const& )
    {
        return taxicab_distance<PN1, PN2>();
    }
};

template <typename P1, typename P2>
struct comparable_type<taxicab_distance<P1, P2> >
{
    typedef taxicab_distance<P1, P2> type;
};

template <typename P1, typename P2>
struct get_comparable<taxicab_distance<P1, P2> >
{
    static inline taxicab_distance<P1, P2> apply(taxicab_distance<P1, P2> const& input)
    {
        return input;
    }
};

template <typename P1, typename P2>
struct result_from_distance<taxicab_distance<P1, P2> >
{
    template <typename T>
    static inline typename coordinate_type<P1>::type apply(taxicab_distance<P1, P2> const& , T const& value)
    {
        return value;
    }
};

}}}}} // namespace bg::strategy::distance::services

template <typename Geometry1, typename Geometry2>
void test_distance(Geometry1 const& geometry1,
            Geometry2 const& geometry2,
            long double expected_distance)
{
    typename bg::default_distance_result<Geometry1>::type distance = bg::distance(geometry1, geometry2);

#ifdef GEOMETRY_TEST_DEBUG
    std::ostringstream out;
    out << typeid(typename bg::coordinate_type<Geometry1>::type).name()
        << std::endl
        << typeid(typename bg::default_distance_result<Geometry1>::type).name()
        << std::endl
        << "distance : " << bg::distance(geometry1, geometry2)
        << std::endl;
    std::cout << out.str();
#endif

    BOOST_CHECK_CLOSE(distance, expected_distance, 0.0001);
}

template <typename Geometry1, typename Geometry2>
void test_geometry(std::string const& wkt1, std::string const& wkt2, double expected_distance)
{
    Geometry1 geometry1;
    bg::read_wkt(wkt1, geometry1);
    Geometry2 geometry2;
    bg::read_wkt(wkt2, geometry2);

    test_distance(geometry1, geometry2, expected_distance);
}

#endif