#pragma once

#include <boost/operators.hpp>
#include <cassert>
#include <tuple>   // import std::tie()
#include <utility> // import std::move

namespace recti
{

/**
 * @brief vector2
 *
 */
template <typename T = int>
class vector2
    : boost::totally_ordered<vector2<T>,
          boost::additive<vector2<T>, boost::multiplicative<vector2<T>, T>>>
// note: private inheritance is OK here!
{
  private:
    T _x;
    T _y;

  public:
    /**
     * @brief
     *
     */
    constexpr vector2(T&& x, T&& y) noexcept
        : _x {std::move(x)}
        , _y {std::move(y)}
    {
    }

    /**
     * @brief
     *
     */
    constexpr vector2(const T& x, const T& y)
        : _x {x}
        , _y {y}
    {
    }

    /**
     * @brief
     *
     * @return constexpr const T&
     */
    [[nodiscard]] constexpr auto x() const noexcept -> const T&
    {
        return this->_x;
    }

    /**
     * @brief
     *
     * @return constexpr const T&
     */
    [[nodiscard]] constexpr auto y() const noexcept -> const T&
    {
        return this->_y;
    }

    /**
     * @brief
     *
     * @param rhs
     * @return constexpr vector2&
     */
    [[nodiscard]] constexpr auto cross(const vector2& rhs) const -> T
    {
        return this->_x * rhs._y - rhs._x * this->_y;
    }

    /**
     * @brief
     *
     * @param rhs
     * @return constexpr vector2&
     */
    constexpr auto operator+=(const vector2& rhs) -> vector2&
    {
        this->_x += rhs.x();
        this->_y += rhs.y();
        return *this;
    }

    /**
     * @brief
     *
     * @param rhs
     * @return constexpr vector2&
     */
    constexpr auto operator-=(const vector2& rhs) -> vector2&
    {
        this->_x -= rhs.x();
        this->_y -= rhs.y();
        return *this;
    }

    /**
     * @brief
     *
     * @param alpha
     * @return constexpr vector2&
     */
    constexpr auto operator*=(const T& alpha) -> vector2&
    {
        this->_x *= alpha;
        this->_y *= alpha;
        return *this;
    }

    /**
     * @brief
     *
     * @param alpha
     * @return constexpr vector2&
     */
    constexpr auto operator/=(const T& alpha) -> vector2&
    {
        this->_x /= alpha;
        this->_y /= alpha;
        return *this;
    }

    /**
     * @brief
     *
     * @param rhs
     * @return true
     * @return false
     */
    constexpr auto operator==(const vector2<T>& rhs) const -> bool
    {
        return std::tie(this->x(), this->y()) == std::tie(rhs.x(), rhs.y());
    }

    /**
     * @brief
     *
     * @param rhs
     * @return true
     * @return false
     */
    constexpr auto operator<(const vector2<T>& rhs) const -> bool
    {
        return std::tie(this->x(), this->y()) < std::tie(rhs.x(), rhs.y());
    }
};

/**
 * @brief 2D point
 *
 * @tparam T1
 * @tparam T2
 */
#pragma pack(push, 1)
template <typename T1, typename T2 = T1>
class point : boost::totally_ordered<point<T1, T2>,
                  boost::additive2<point<T1, T2>, vector2<T1>>>
{
  protected:
    T1 _x; //!< x coordinate
    T2 _y; //!< y coordinate

  public:
    /**
     * @brief Construct a new point object
     *
     * @param x
     * @param y
     */
    constexpr point(T1&& x, T2&& y) noexcept
        : _x {std::move(x)}
        , _y {std::move(y)}
    {
    }

    /**
     * @brief Construct a new point object
     *
     * @param x
     * @param y
     */
    constexpr point(const T1& x, const T2& y)
        : _x {x}
        , _y {y}
    {
    }

    /**
     * @brief
     *
     * @return const T1&
     */
    [[nodiscard]] constexpr auto x() const noexcept -> const T1&
    {
        return this->_x;
    }

    /**
     * @brief
     *
     * @return const T2&
     */
    [[nodiscard]] constexpr auto y() const noexcept -> const T2&
    {
        return this->_y;
    }

    /**
     * @brief
     *
     * @param rhs
     * @return constexpr point&
     */
    constexpr auto operator+=(const vector2<T1>& rhs) -> point&
    {
        this->_x += rhs.x();
        this->_y += rhs.y();
        return *this;
    }

    /**
     * @brief
     *
     * @param rhs
     * @return constexpr point&
     */
    constexpr auto operator-=(const vector2<T1>& rhs) -> point&
    {
        this->_x -= rhs.x();
        this->_y -= rhs.y();
        return *this;
    }

    /**
     * @brief
     *
     * @param rhs
     * @return vector2<T1>
     */
    constexpr auto operator-(const point& rhs) const -> vector2<T1>
    {
        return {this->x() - rhs.x(), this->y() - rhs.y()};
    }

    /**
     * @brief
     *
     * @tparam U1
     * @tparam U2
     * @param rhs
     * @return true
     * @return false
     */
    template <typename U1, typename U2>
    constexpr auto operator<(const point<U1, U2>& rhs) const -> bool
    {
        return std::tie(this->x(), this->y()) < std::tie(rhs.x(), rhs.y());
    }

    /**
     * @brief
     *
     * @tparam U1
     * @tparam U2
     * @param rhs
     * @return true
     * @return false
     */
    template <typename U1, typename U2>
    constexpr auto operator==(const point<U1, U2>& rhs) const -> bool
    {
        return std::tie(this->x(), this->y()) == std::tie(rhs.x(), rhs.y());
    }

    /**
     * @brief
     *
     * @return point<T2, T1>
     */
    [[nodiscard]] constexpr auto flip() const -> point<T2, T1>
    {
        return {this->y(), this->x()};
    }

    /**
     * @brief
     *
     * @tparam Stream
     * @tparam T1
     * @tparam T2
     * @param out
     * @param p
     * @return Stream&
     */
    template <class Stream>
    friend auto operator<<(Stream& out, const point& p) -> Stream&
    {
        out << '(' << p.x() << ", " << p.y() << ')';
        return out;
    }
};
#pragma pack(pop)

/**
 * @brief 2D point
 *
 * @tparam T1
 * @tparam T2
 */
#pragma pack(push, 1)
template <typename T1, typename T2 = T1>
class dualpoint : public point<T1, T2>
{
  public:
    /**
     * @brief
     *
     * @return const T1&
     */
    constexpr auto y() const -> const T1& // override intentionally
    {
        return this->_x;
    }

    /**
     * @brief
     *
     * @return const T2&
     */
    constexpr auto x() const -> const T2& // override intentionally
    {
        return this->_y;
    }
};
#pragma pack(pop)

/**
 * @brief adapter for containers of point
 *
 * @tparam iter
 */
template <typename iterator>
class dual_iterator : public iterator
{
    using value_type = typename iterator::value_type;
    using T1 = decltype(std::declval(iterator::value_type).x());
    using T2 = decltype(std::declval(iterator::value_type).y());

    constexpr dual_iterator(iterator&& a)
        : iterator {std::forward<iterator>(a)}
    {
    }

    constexpr auto operator*() const noexcept -> const dualpoint<T2, T1>&
    {
        return dualpoint<T2, T1> {};
        // return std::reinterpret_cast<const dualpoint<T2,
        // T1>&>(*iterator::operator*());
    }

    constexpr auto operator*() noexcept -> dualpoint<T2, T1>&
    {
        return dualpoint<T2, T1> {};
        // return std::reinterpret_cast<dualpoint<T2,
        // T1>&>(*iterator::operator*());
    }
};

/**
 * @brief Interval
 *
 * @tparam T
 */
#pragma pack(push, 1)
template <typename T = int>
class interval : boost::totally_ordered<interval<T>>
{
  private:
    T _lower; //> lower bound
    T _upper; //> upper bound

  public:
    /**
     * @brief Construct a new interval object
     *
     * @param lower
     * @param upper
     */
    constexpr interval(T&& lower, T&& upper) noexcept
        : _lower {std::move(lower)}
        , _upper {std::move(upper)}
    {
        assert(!(_upper < _lower));
    }

    /**
     * @brief Construct a new interval object
     *
     * @param lower
     * @param upper
     */
    constexpr interval(const T& lower, const T& upper)
        : _lower {lower}
        , _upper {upper}
    {
        assert(!(_upper < _lower));
    }

    /**
     * @brief
     *
     * @return const T&
     */
    [[nodiscard]] constexpr auto lower() const -> const T&
    {
        return this->_lower;
    }

    /**
     * @brief
     *
     * @return const T&
     */
    [[nodiscard]] constexpr auto upper() const -> const T&
    {
        return this->_upper;
    }

    /**
     * @brief
     *
     * @return constexpr T
     */
    [[nodiscard]] constexpr auto len() const -> T
    {
        return this->upper() - this->lower();
    }

    /**
     * @brief
     *
     * @param rhs
     * @return true
     * @return false
     */
    constexpr auto operator==(const interval& rhs) const -> bool
    {
        return this->lower() == rhs.lower() && this->upper() == rhs.upper();
    }

    /**
     * @brief
     *
     * @param rhs
     * @return true
     * @return false
     */
    constexpr auto operator<(const interval& rhs) const -> bool
    {
        return this->upper() < rhs.lower();
    }

    /**
     * @brief
     *
     * @tparam U
     * @param a
     * @return true
     * @return false
     */
    template <typename U>
    [[nodiscard]] constexpr auto contains(const interval<U>& a) const -> bool
    {
        return !(a.lower() < this->lower() || this->upper() < a.upper());
    }

    /**
     * @brief
     *
     * @param x
     * @return true
     * @return false
     */
    [[nodiscard]] constexpr auto contains(const T& a) const -> bool
    {
        return !(a < this->lower() || this->upper() < a);
    }
};
#pragma pack(pop)

/**
 * @brief Rectangle (Rectilinear)
 *
 * @tparam T
 * @todo use "__attribute__((aligned(0)))" to align struct 'rectangle<int>' to 0
 * bytes
 */
#pragma pack(push, 1)
template <typename T>
struct rectangle : point<interval<T>>
{
    /**
     * @brief Construct a new rectangle object
     *
     * @param x
     * @param y
     */
    constexpr rectangle(interval<T>&& x, interval<T>&& y) noexcept
        : point<interval<T>> {std::move(x), std::move(y)}
    {
    }

    /**
     * @brief Construct a new rectangle object
     *
     * @param x
     * @param y
     */
    constexpr rectangle(const interval<T>& x, const interval<T>& y)
        : point<interval<T>> {x, y}
    {
    }

    /**
     * @brief
     *
     * @param rhs
     * @return true
     * @return false
     */
    template <typename U1, typename U2>
    [[nodiscard]] constexpr auto contains(const point<U1, U2>& rhs) const
        -> bool
    {
        return this->x().contains(rhs.x()) && this->y().contains(rhs.y());
    }

    /**
     * @brief
     *
     * @return point<T>
     */
    [[nodiscard]] constexpr auto lower() const -> point<T>
    {
        return {this->x().lower(), this->y().lower()};
    }

    /**
     * @brief
     *
     * @return point<T>
     */
    [[nodiscard]] constexpr auto upper() const -> point<T>
    {
        return {this->x().upper(), this->y().upper()};
    }

    /**
     * @brief
     *
     * @return constexpr T
     */
    [[nodiscard]] constexpr auto area() const -> T
    {
        return this->x().len() * this->y().len();
    }

    /**
     * @brief
     *
     * @tparam Stream
     * @tparam T
     * @param out
     * @param r
     * @return Stream&
     */
    template <class Stream>
    friend auto operator<<(Stream& out, const rectangle& r) -> Stream&
    {
        out << r.lower() << " rectangle " << r.upper();
        return out;
    }
};
#pragma pack(pop)

/**
 * @brief Horizontal Line Segment
 *
 * @tparam T
 * @todo pack
 */
#pragma pack(push, 1)
template <typename T>
struct hsegment : point<interval<T>, T>
{
    /**
     * @brief Construct a new hsegment object
     *
     * @param x
     * @param y
     */
    constexpr hsegment(interval<T>&& x, T&& y) noexcept
        : point<interval<T>, T> {std::move(x), std::move(y)}
    {
    }

    /**
     * @brief Construct a new hsegment object
     *
     * @param x
     * @param y
     */
    constexpr hsegment(const interval<T>& x, const T& y)
        : point<interval<T>, T> {x, y}
    {
    }

    /**
     * @brief
     *
     * @tparam U
     * @param rhs
     * @return true
     * @return false
     */
    template <typename U>
    constexpr auto contains(const point<U>& rhs) const -> bool
    {
        return this->y() == rhs.y() && this->x().contains(rhs.x());
    }
};
#pragma pack(pop)

/**
 * @brief vsegment Line Segment
 *
 * @tparam T
 * @todo pack
 */
#pragma pack(push, 1)
template <typename T>
struct vsegment : point<T, interval<T>>
{
    /**
     * @brief Construct a new vsegment object
     *
     * @param x
     * @param y
     */
    constexpr vsegment(T&& x, interval<T>&& y) noexcept
        : point<T, interval<T>> {std::move(x), std::move(y)}
    {
    }

    /**
     * @brief Construct a new vsegment object
     *
     * @param x
     * @param y
     */
    constexpr vsegment(const T& x, const interval<T>& y)
        : point<T, interval<T>> {x, y}
    {
    }

    /**
     * @brief
     *
     * @tparam U
     * @param rhs
     * @return true
     * @return false
     */
    template <typename U>
    constexpr auto contains(const point<U>& rhs) const -> bool
    {
        return this->x() == rhs.x() && this->y().contains(rhs.y());
    }
};
#pragma pack(pop)

} // namespace recti


#include <CGAL/trace.h>
#include <CGAL/Timer.h>
#include <iostream>
#include <string>
#include <fstream>

#include <Eigen/Eigen>
#include <Eigen/SVD>

int main() {

  std::ifstream file;
  file.open("SVD_benchmark");
  if (!file)
  {
    CGAL_TRACE_STREAM << "Error loading file!\n";
    return 0;
  }

  int ite = 200000;
  Eigen::JacobiSVD<Eigen::Matrix3d> svd;
  Eigen::Matrix3d u, v, cov, r;
  Eigen::Vector3d w;

  int matrix_idx = rand()%200;
  for (int i = 0; i < matrix_idx; i++)
  {
    for (int j = 0; j < 3; j++)
    {
      for (int k = 0; k < 3; k++)
      {
        file >> cov(j, k);
      }
    }
  }

  CGAL::Timer task_timer;

  CGAL_TRACE_STREAM << "Start SVD decomposition...";
  task_timer.start();
  for (int i = 0; i < ite; i++)
  {

    svd.compute( cov, Eigen::ComputeFullU | Eigen::ComputeFullV );
    u = svd.matrixU(); v = svd.matrixV(); w = svd.singularValues();
    r = v*u.transpose();
  }
  task_timer.stop();
  file.close();

  CGAL_TRACE_STREAM << "done: " << task_timer.time() << "s\n";

  return 0;
}


//==============================================================================
//         Copyright 2003 - 2013   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2013   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#include <nt2/include/functions/tan.hpp>

#include <boost/dispatch/functor/meta/call.hpp>
#include <nt2/sdk/functor/meta/call.hpp>
#include <nt2/sdk/unit/tests/relation.hpp>
#include <nt2/sdk/unit/tests/type_expr.hpp>
#include <complex>
#include <nt2/sdk/complex/complex.hpp>
#include <nt2/sdk/unit/tests/ulp.hpp>
#include <nt2/sdk/unit/tests/basic.hpp>
#include <nt2/sdk/meta/as_integer.hpp>
#include <nt2/sdk/unit/module.hpp>
#include <boost/simd/sdk/config.hpp>

#include <nt2/include/constants/pi.hpp>
#include <nt2/include/constants/zero.hpp>
#include <nt2/include/constants/inf.hpp>
#include <nt2/include/constants/minf.hpp>
#include <nt2/include/constants/nan.hpp>
#include <nt2/include/constants/i.hpp>

#include <nt2/include/functions/mul_i.hpp>
#include <nt2/include/functions/mul_minus_i.hpp>

NT2_TEST_CASE_TPL ( tan,  NT2_REAL_TYPES)
{
  using nt2::tan;
  using nt2::tag::tan_;
  typedef std::complex<T> cT;
  typedef typename nt2::meta::call<tan_(cT)>::type r_t;
  typedef typename nt2:: meta::as_complex<T>::type wished_r_t;

  NT2_TEST_TYPE_IS(r_t, wished_r_t);

  // specific values tests
  NT2_TEST_ULP_EQUAL(nt2::tan(cT(nt2::Inf<T>())), cT(nt2::Nan<T>()), 10);
  NT2_TEST_ULP_EQUAL(nt2::tan(cT(nt2::Minf<T>())), cT(nt2::Nan<T>()), 10);
  NT2_TEST_ULP_EQUAL(nt2::tan(cT(1, 1)),std::tan(cT(1.0, 1.0)), 10);
  NT2_TEST_ULP_EQUAL(nt2::tan(cT(1, 0.5)),std::tan(cT(1.0, 0.5)), 10);
  NT2_TEST_ULP_EQUAL(nt2::tan(cT(0.5, 1)),std::tan(cT(0.5, 1.0)), 10);
  NT2_TEST_ULP_EQUAL(nt2::tan(cT(0.5, 0.5)),std::tan(cT(0.5, 0.5)), 10);
  NT2_TEST_ULP_EQUAL(nt2::tan(cT(0, 1)),std::tan(cT(0.0, 1.0)), 10);
  NT2_TEST_ULP_EQUAL(nt2::tan(cT(0, 0.5)),std::tan(cT(0.0, 0.5)), 10);
  NT2_TEST_ULP_EQUAL(nt2::tan(cT(0.5, 0)),std::tan(cT(0.5, 0.0)), 10);

  const int N = 20;
  cT inputs[N] =
    { cT(nt2::Zero<T>(),nt2::Zero<T>()),cT(nt2::Inf<T>(),nt2::Zero<T>()),cT(nt2::Minf<T>(),nt2::Zero<T>()),cT(nt2::Nan<T>(),nt2::Zero<T>()),
      cT(nt2::Zero<T>(),nt2::Inf<T>()), cT(nt2::Inf<T>(),nt2::Inf<T>()), cT(nt2::Minf<T>(),nt2::Inf<T>()), cT(nt2::Nan<T>(),nt2::Inf<T>()),
      cT(nt2::Zero<T>(),nt2::Minf<T>()),cT(nt2::Inf<T>(),nt2::Minf<T>()),cT(nt2::Minf<T>(),nt2::Minf<T>()),cT(nt2::Nan<T>(),nt2::Minf<T>()),
      cT(nt2::Zero<T>(),nt2::Nan<T>()), cT(nt2::Inf<T>(),nt2::Nan<T>()), cT(nt2::Minf<T>(),nt2::Nan<T>()), cT(nt2::Nan<T>(),nt2::Nan<T>()),
      cT(nt2::Zero<T>(),nt2::Pi <T>()), cT(nt2::Inf<T>(),nt2::Pi <T>()), cT(nt2::Minf<T>(),nt2::Pi <T>()), cT(nt2::Nan<T>(),nt2::Pi<T>()),
    };

  for(int i=0; i < N; i++)
   {
     std::cout << "-------------------" << std::endl;
     std::cout << "inputs  "<< inputs[i] << std::endl;
     NT2_TEST_ULP_EQUAL(nt2::tan(-inputs[i]), -nt2::tan(inputs[i]), 3);
     NT2_TEST_ULP_EQUAL(nt2::tan(inputs[i]), nt2::mul_minus_i(nt2::tanh(nt2::mul_i(inputs[i]))), 3);
     std::cout << "=================== " << std::endl;
   }

 } // end of test for floating_


//Author: Dr. Shantanu Shahane
#ifndef general_functions_H_ /* Include guard */
#define general_functions_H_
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <float.h>
#include <string.h>
#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>
#include <fstream>
#include <random>
#include "metis.h"
#include "mpi.h"
#include <Eigen/Dense>
#include <unsupported/Eigen/SparseExtra>
#include <Eigen/SparseLU>
#include <Eigen/OrderingMethods>
#include <Eigen/Eigenvalues>
#include <Eigen/Core>
#include <Eigen/SparseCore>
#include <Spectra/GenEigsSolver.h>
#include <Spectra/MatOp/SparseGenMatProd.h>
#include <Spectra/GenEigsRealShiftSolver.h>
#include <Spectra/MatOp/SparseGenRealShiftSolve.h>
using namespace std;

Eigen::VectorXcd calc_largest_magnitude_eigenvalue(Eigen::SparseMatrix<double, Eigen::RowMajor> &matrix);

void does_file_exist(const char *, const char *);

void check_mpi();

void print_to_terminal(vector<bool> &a, const char *text);

void print_to_terminal(vector<double> &a, int n_row, int n_col, const char *text);

void print_to_terminal(vector<double> &a, const char *text);

void print_to_terminal(vector<int> &a, const char *text);

void print_to_terminal(Eigen::VectorXd &a, const char *text);

void print_to_terminal(vector<vector<int>> &a, const char *text);

void print_to_terminal(vector<pair<int, int>> &a, const char *text);

void print_to_terminal(vector<int> &a, int n_row, int n_col, const char *text);

void print_to_terminal(idx_t *elem_vert_row, idx_t *elem_vert_col, int n_row, const char *text);

void print_to_terminal(vector<bool> &a, int n_row, int n_col, const char *text);

void print_to_terminal(vector<int> &sp_row, vector<int> &sp_col, const char *text);

void print_to_terminal(Eigen::MatrixXd &A, const char *text);

void print_to_terminal(Eigen::MatrixXcd &A, const char *text);

void print_to_terminal(Eigen::MatrixXi &A, const char *text);

void print_to_terminal(Eigen::SparseMatrix<double, Eigen::RowMajor> &A, const char *text);

void print_to_terminal(Eigen::SparseMatrix<int> &A, const char *text);

void write_csv(vector<double> &vect, int nr, int nc, const char *file_name);

void write_csv(vector<bool> &vect, int nr, int nc, const char *file_name);

void write_csv(double *vect, int nr, int nc, const char *file_name);

void write_csv(Eigen::MatrixXd &A, const char *file_name);

void write_csv(Eigen::MatrixXcd &A, const char *file_name);

void write_csv(Eigen::VectorXd &A, const char *file_name);

void write_csv_benchmark(vector<double> &xyz_interp, Eigen::VectorXd &reference, Eigen::VectorXd &simulation, const char *file_name, int dimension);

void write_csv_benchmark(vector<double> &xyz_interp, Eigen::VectorXd &simulation, const char *file_name, int dimension);

void write_csv(vector<double> &xyz, vector<bool> &boundary_flag, int dim, const char *file_name);

void write_csv(vector<double> &xyz, vector<bool> &boundary_flag, vector<bool> &periodic_bc_flag, int dim, const char *file_name);

void write_csv(vector<double> &xyz, vector<bool> &boundary_flag, vector<bool> &periodic_bc_flag, vector<int> &periodic_bc_section, int dim, const char *file_name);

void write_csv(vector<double> &xyz, vector<bool> &boundary_flag, Eigen::VectorXd &A_ana, Eigen::VectorXd &A_num, int dim, const char *file_name);

void write_csv(Eigen::SparseMatrix<double, Eigen::RowMajor> &A, const char *file_name);

void write_csv(Eigen::SparseMatrix<int> &A, const char *file_name);

void write_csv(vector<int> &sp_row, vector<int> &sp_col, const char *file_name);

void write_csv(vector<int> &sp_row, vector<double> &sp_val, const char *file_name);

void write_csv(vector<vector<int>> &a, const char *file_name);

void write_csv(vector<vector<double>> &a, const char *file_name);

void write_csv(vector<tuple<int, int, double>> &a, const char *file_name);

void write_csv(vector<int> &vect, int nr, int nc, const char *file_name);

void k_smallest_elements(vector<double> &k_min_a, vector<int> &k_min_a_indices, vector<double> &a, int k);

vector<int> argsort(const vector<double> &v);

vector<double> calc_crowd_distance(vector<double> &x, vector<double> &y, vector<double> &z, int dim);

double max_abs(Eigen::VectorXd &a);

void cuthill_mckee_ordering(vector<vector<int>> &adjacency, vector<int> &order);

void reverse_cuthill_mckee_ordering(vector<vector<int>> &adjacency, vector<int> &order);

double vector_norm(vector<double> &a, int norm_type);

double vector_norm(double *a, int size, int norm_type);

void cross_product(double *result, double *u, double *v);

void calc_max_l1_error(vector<double> &a1, vector<double> &a2, double &max_err, double &l1_err);

void calc_max_l1_error(Eigen::VectorXd &a1, Eigen::VectorXd &a2, double &max_err, double &l1_err);

void calc_max_l1_error(Eigen::VectorXd &a1, Eigen::VectorXd &a2, double &max_err_boundary, double &l1_err_boundary, double &max_err_internal, double &l1_err_internal, vector<bool> &boundary_flag);

void calc_max_l1_relative_error(vector<double> &ana_val, vector<double> &num_val, double &max_err, double &l1_err);

void calc_max_l1_relative_error(Eigen::VectorXd &ana_val, Eigen::VectorXd &num_val, double &max_err, double &l1_err);

void calc_max_l1_relative_error(Eigen::VectorXd &ana_val, Eigen::VectorXd &num_val, double &max_err_boundary, double &l1_err_boundary, double &max_err_internal, double &l1_err_internal, vector<bool> &boundary_flag);

void gauss_siedel_eigen(Eigen::SparseMatrix<double, Eigen::RowMajor> &matrix, Eigen::VectorXd &source, Eigen::VectorXd &field_old, int num_iter, double omega);

Eigen::SparseMatrix<double, Eigen::RowMajor> convert_csc_to_csr_eigen(Eigen::SparseMatrix<double, Eigen::ColMajor> &matrix);

Eigen::SparseMatrix<double, Eigen::ColMajor> convert_csr_to_csc_eigen(Eigen::SparseMatrix<double, Eigen::RowMajor> &matrix);

#endif


#pragma once
#include <boost/numeric/ublas/io.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/matrix_proxy.hpp>

using namespace boost::numeric::ublas;

template <typename T>
void fp2double(T x_fp, double &x, size_t &p) {
    x = double((int32_t) x_fp) / (1ll << p);
    //cout << "fp2double(" << x_fp << "," << p << ") = " << x << endl;
}

template <typename T>
void double2fp(double x, T &x_fp, size_t &p) {
    x_fp = (x * (1ll << p));
    /*cout << "double2fp(" << x << "," << p << ") = " << x_fp << endl;
    // test:
    double x_test = 99.99;
    fp2double(x_fp, x_test, p);*/
}

// This assumes memory for M_fp has been allocated
template <typename T>
void double_matrix2fp(matrix<double> &M, matrix<T> &M_fp, size_t precision) {
    for (size_t i = 0; i < M.size1(); i++) {
        for (size_t j = 0; j < M.size2(); j++) {
        double2fp(M(i, j), M_fp(i, j), precision);
        }
    }
}

// This assumes memory for M_fp has been allocated
template <typename T>
void fp_matrix2double(matrix<T> &M_fp, matrix<double> &M, size_t precision) {
    for (size_t i = 0; i < M.size1(); i++) {
        for (size_t j = 0; j < M.size2(); j++) {
        fp2double(M_fp(i, j), M(i, j), precision);
        }
    }
}


// Boost.Geometry
// Unit Test

// Copyright (c) 2018, Oracle and/or its affiliates.
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

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

#include <geometry_test_common.hpp>

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/geometries.hpp>

int test_main(int, char* [])
{
    typedef bg::model::point<double, 2, bg::cs::cartesian> point;
    typedef bg::model::box<point> box;
    typedef bg::model::linestring<point> linestring;
    typedef bg::model::multi_linestring<linestring> mlinestring;
    typedef bg::model::polygon<point> polygon;
    typedef bg::model::multi_polygon<polygon> mpolygon;

    point p;
    linestring ls;
    mlinestring mls;
    polygon po;
    mpolygon mpo;

    bg::read_wkt("POINT(0 0)", p);
    bg::read_wkt("LINESTRING(0 0,7 7,7 9)", ls);
    bg::read_wkt("MULTILINESTRING((0 0,7 7,7 9),(7 9, 9 9))", mls);
    bg::read_wkt("POLYGON((0 0,0 5,5 5,5 0,0 0),(1 1,4 1,4 4,1 4,1 1))", po);
    bg::read_wkt("MULTIPOLYGON(((0 0,0 5,5 5,5 0,0 0),(1 1,4 1,4 4,1 4,1 1)),((2 2,2 3,3 3,3 2,2 2)))", mpo);

    BOOST_CHECK_CLOSE(bg::perimeter(po), 32.0, 0.0001);
    BOOST_CHECK_CLOSE(bg::area(mpo), 17.0, 0.0001);
    BOOST_CHECK_CLOSE(bg::length(mls), 13.899494936611665, 0.0001);

    BOOST_CHECK(bg::covered_by(p, po));
    BOOST_CHECK(!bg::crosses(ls, mls));
    BOOST_CHECK(!bg::equals(ls, mls));
    BOOST_CHECK(bg::intersects(ls, po));
    BOOST_CHECK(bg::relate(p, ls, bg::de9im::mask("F0F******")));
    BOOST_CHECK(bg::relation(mls, mpo).str() == "101F00212");
    BOOST_CHECK(bg::within(po, mpo));
    BOOST_CHECK(!bg::touches(mls, po));

    mpolygon res;
    bg::intersection(po, mpo, res);
    BOOST_CHECK_CLOSE(bg::area(res), 16.0, 0.0001);
    bg::clear(res);
    bg::union_(po, mpo, res);
    BOOST_CHECK_CLOSE(bg::area(res), 17.0, 0.0001);
    bg::clear(res);
    bg::difference(mpo, po, res);
    BOOST_CHECK_CLOSE(bg::area(res), 1.0, 0.0001);
    bg::clear(res);
    bg::sym_difference(mpo, po, res);
    BOOST_CHECK_CLOSE(bg::area(res), 1.0, 0.0001);

    BOOST_CHECK(bg::is_simple(ls));
    BOOST_CHECK(bg::is_valid(mpo));

    point c;
    bg::centroid(mpo, c);
    BOOST_CHECK_CLOSE(bg::distance(p, c), 3.5355339059327378, 0.0001);
    BOOST_CHECK_CLOSE(bg::distance(mls, mpo), 0.0, 0.0001);
    BOOST_CHECK_CLOSE(bg::distance(po, mpo), 0.0, 0.0001);

    box b;
    bg::envelope(mls, b);
    BOOST_CHECK_CLOSE(bg::area(b), 81.0, 0.0001);

    polygon h;
    bg::convex_hull(mls, h);
    BOOST_CHECK_CLOSE(bg::area(h), 9.0, 0.0001);

    return 0;
}


// Copyright (C) 2012  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.
#include <dlib/svm.h>
#include <dlib/rand.h>
#include <sstream>
#include <string>
#include <cstdlib>
#include <ctime>

#include "tester.h"

namespace
{

    using namespace test;
    using namespace dlib;
    using namespace std;

    logger dlog("test.ranking");

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

    template <typename T>
    void brute_force_count_ranking_inversions (
        const std::vector<T>& x,
        const std::vector<T>& y,
        std::vector<unsigned long>& x_count,
        std::vector<unsigned long>& y_count
    )
    {
        x_count.assign(x.size(),0);
        y_count.assign(y.size(),0);

        for (unsigned long i = 0; i < x.size(); ++i)
        {
            for (unsigned long j = 0; j < y.size(); ++j)
            {
                if (x[i] <= y[j])
                {
                    x_count[i]++;
                    y_count[j]++;
                }
            }
        }
    }

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

    void test_count_ranking_inversions()
    {
        print_spinner();
        dlog << LINFO << "in test_count_ranking_inversions()";

        dlib::rand rnd;
        std::vector<int> x, y;
        std::vector<unsigned long> x_count, y_count;
        std::vector<unsigned long> x_count2, y_count2;
        for (int iter = 0; iter < 5000; ++iter)
        {
            x.resize(rnd.get_random_32bit_number()%10);
            y.resize(rnd.get_random_32bit_number()%10);
            for (unsigned long i = 0; i < x.size(); ++i)
                x[i] = ((int)rnd.get_random_32bit_number()%10) - 5;
            for (unsigned long i = 0; i < y.size(); ++i)
                y[i] = ((int)rnd.get_random_32bit_number()%10) - 5;

            count_ranking_inversions(x, y, x_count, y_count);
            brute_force_count_ranking_inversions(x, y, x_count2, y_count2);

            DLIB_TEST(mat(x_count) == mat(x_count2));
            DLIB_TEST(mat(y_count) == mat(y_count2));
        }
    }

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

    void dotest1()
    {
        print_spinner();
        dlog << LINFO << "in dotest1()";

        typedef matrix<double,4,1> sample_type;

        typedef linear_kernel<sample_type> kernel_type;

        svm_rank_trainer<kernel_type> trainer;

        std::vector<ranking_pair<sample_type> > samples;

        ranking_pair<sample_type> p;
        sample_type samp;

        samp = 0, 0, 0, 1; p.relevant.push_back(samp);
        samp = 1, 0, 0, 0; p.nonrelevant.push_back(samp);
        samples.push_back(p);

        samp = 0, 0, 1, 0; p.relevant.push_back(samp);
        samp = 1, 0, 0, 0; p.nonrelevant.push_back(samp);
        samp = 0, 1, 0, 0; p.nonrelevant.push_back(samp);
        samp = 0, 1, 0, 0; p.nonrelevant.push_back(samp);
        samples.push_back(p);

        trainer.set_c(10);

        decision_function<kernel_type> df = trainer.train(samples);

        dlog << LINFO << "accuracy: "<< test_ranking_function(df, samples);
        matrix<double,1,2> res;
        res = 1,1;
        DLIB_TEST(equal(test_ranking_function(df, samples), res));

        DLIB_TEST(equal(test_ranking_function(trainer.train(samples[1]), samples), res));

        trainer.set_epsilon(1e-13);
        df = trainer.train(samples);

        dlog << LINFO << df.basis_vectors(0);
        sample_type truew;
        truew = -0.5, -0.5, 0.5, 0.5;
        DLIB_TEST(length(truew - df.basis_vectors(0)) < 1e-10);

        dlog << LINFO << "accuracy: "<< test_ranking_function(df, samples);
        DLIB_TEST(equal(test_ranking_function(df, samples), res));

        dlog << LINFO << "cv-accuracy: "<< cross_validate_ranking_trainer(trainer, samples,2);
        DLIB_TEST(std::abs(cross_validate_ranking_trainer(trainer, samples,2)(0) - 0.7777777778) < 0.0001);

        trainer.set_learns_nonnegative_weights(true);
        df = trainer.train(samples);
        truew = 0, 0, 1.0, 1.0;
        dlog << LINFO << df.basis_vectors(0);
        DLIB_TEST(length(truew - df.basis_vectors(0)) < 1e-10);
        dlog << LINFO << "accuracy: "<< test_ranking_function(df, samples);
        DLIB_TEST(equal(test_ranking_function(df, samples), res));

        samples.clear();
        samples.push_back(p);
        samples.push_back(p);
        samples.push_back(p);
        samples.push_back(p);
        dlog << LINFO << "cv-accuracy: "<< cross_validate_ranking_trainer(trainer, samples,4);
        DLIB_TEST(equal(cross_validate_ranking_trainer(trainer, samples,4) , res));

        df.basis_vectors(0) = 0;
        dlog << LINFO << "BAD RANKING:" << test_ranking_function(df, samples);
        DLIB_TEST(test_ranking_function(df, samples)(1) < 0.5);
    }

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

    void dotest_sparse_vectors()
    {
        print_spinner();
        dlog << LINFO << "in dotest_sparse_vectors()";

        typedef std::map<unsigned long,double> sample_type;

        typedef sparse_linear_kernel<sample_type> kernel_type;

        svm_rank_trainer<kernel_type> trainer;

        std::vector<ranking_pair<sample_type> > samples;

        ranking_pair<sample_type> p;
        sample_type samp;

        samp[3] = 1; p.relevant.push_back(samp); samp.clear();
        samp[0] = 1; p.nonrelevant.push_back(samp); samp.clear();
        samples.push_back(p);

        samp[2] = 1; p.relevant.push_back(samp); samp.clear();
        samp[0] = 1; p.nonrelevant.push_back(samp); samp.clear();
        samp[1] = 1; p.nonrelevant.push_back(samp); samp.clear();
        samp[1] = 1; p.nonrelevant.push_back(samp); samp.clear();
        samples.push_back(p);

        trainer.set_c(10);

        decision_function<kernel_type> df = trainer.train(samples);

        matrix<double,1,2> res;
        res = 1,1;

        dlog << LINFO << "accuracy: "<< test_ranking_function(df, samples);
        DLIB_TEST(equal(test_ranking_function(df, samples), res));

        DLIB_TEST(equal(test_ranking_function(trainer.train(samples[1]), samples), res));

        trainer.set_epsilon(1e-13);
        df = trainer.train(samples);

        dlog << LINFO << sparse_to_dense(df.basis_vectors(0));
        sample_type truew;
        truew[0] = -0.5;
        truew[1] = -0.5;
        truew[2] =  0.5;
        truew[3] =  0.5;
        DLIB_TEST(length(subtract(truew , df.basis_vectors(0))) < 1e-10);

        dlog << LINFO << "accuracy: "<< test_ranking_function(df, samples);
        DLIB_TEST(equal(test_ranking_function(df, samples), res));

        dlog << LINFO << "cv-accuracy: "<< cross_validate_ranking_trainer(trainer, samples,2);
        DLIB_TEST(std::abs(cross_validate_ranking_trainer(trainer, samples,2)(0) - 0.7777777778) < 0.0001);

        trainer.set_learns_nonnegative_weights(true);
        df = trainer.train(samples);
        truew[0] =  0.0;
        truew[1] =  0.0;
        truew[2] =  1.0;
        truew[3] =  1.0;
        dlog << LINFO << sparse_to_dense(df.basis_vectors(0));
        DLIB_TEST(length(subtract(truew , df.basis_vectors(0))) < 1e-10);
        dlog << LINFO << "accuracy: "<< test_ranking_function(df, samples);
        DLIB_TEST(equal(test_ranking_function(df, samples), res));

        samples.clear();
        samples.push_back(p);
        samples.push_back(p);
        samples.push_back(p);
        samples.push_back(p);
        dlog << LINFO << "cv-accuracy: "<< cross_validate_ranking_trainer(trainer, samples,4);
        DLIB_TEST(equal(cross_validate_ranking_trainer(trainer, samples,4) , res) );
    }

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

    template <typename K, bool use_dcd_trainer>
    class simple_rank_trainer
    {
    public:
        template <typename T>
        decision_function<K> train (
            const ranking_pair<T>& pair
        ) const
        {
            typedef matrix<double,10,1> sample_type;

            std::vector<sample_type> relevant = pair.relevant;
            std::vector<sample_type> nonrelevant = pair.nonrelevant;

            std::vector<sample_type> samples;
            std::vector<double> labels;
            for (unsigned long i = 0; i < relevant.size(); ++i)
            {
                for (unsigned long j = 0; j < nonrelevant.size(); ++j)
                {
                    samples.push_back(relevant[i] - nonrelevant[j]);
                    labels.push_back(+1);
                    samples.push_back(nonrelevant[i] - relevant[j]);
                    labels.push_back(-1);
                }
            }

            if (use_dcd_trainer)
            {
                svm_c_linear_dcd_trainer<K> trainer;
                trainer.set_c(1.0/samples.size());
                trainer.set_epsilon(1e-10);
                trainer.force_last_weight_to_1(true);
                //trainer.be_verbose();
                return trainer.train(samples, labels);
            }
            else
            {
                svm_c_linear_trainer<K> trainer;
                trainer.set_c(1.0);
                trainer.set_epsilon(1e-13);
                trainer.force_last_weight_to_1(true);
                //trainer.be_verbose();
                decision_function<K> df = trainer.train(samples, labels);
                DLIB_TEST_MSG(df.b == 0, df.b);
                return df;
            }
        }
    };

    template <bool use_dcd_trainer>
    void test_svmrank_weight_force_dense()
    {
        print_spinner();
        dlog << LINFO << "use_dcd_trainer: "<< use_dcd_trainer;

        typedef matrix<double,10,1> sample_type;
        typedef linear_kernel<sample_type> kernel_type;

        ranking_pair<sample_type> pair;

        for (int i = 0; i < 20; ++i)
        {
            pair.relevant.push_back(abs(gaussian_randm(10,1,i)));
        }

        for (int i = 0; i < 20; ++i)
        {
            pair.nonrelevant.push_back(-abs(gaussian_randm(10,1,i+10000)));
            pair.nonrelevant.back()(9) += 1;
        }

        svm_rank_trainer<kernel_type> trainer;
        trainer.force_last_weight_to_1(true);
        trainer.set_epsilon(1e-13);
        //trainer.be_verbose();
        decision_function<kernel_type> df;
        df = trainer.train(pair);

        matrix<double,1,2> res;
        res = 1,1;
        dlog << LINFO << "weights: "<< trans(df.basis_vectors(0));
        const matrix<double,1,2> acc1 = test_ranking_function(df, pair);
        dlog << LINFO << "ranking accuracy: " << acc1;
        DLIB_TEST(equal(acc1,res));

        simple_rank_trainer<kernel_type,use_dcd_trainer> strainer;
        decision_function<kernel_type> df2;
        df2 = strainer.train(pair);
        dlog << LINFO << "weights: "<< trans(df2.basis_vectors(0));
        const matrix<double,1,2> acc2 = test_ranking_function(df2, pair);
        dlog << LINFO << "ranking accuracy: " << acc2;
        DLIB_TEST(equal(acc2,res));

        dlog << LINFO << "w error: " << max(abs(df.basis_vectors(0) - df2.basis_vectors(0)));
        dlog << LINFO << "b error: " << abs(df.b - df2.b);
        DLIB_TEST(std::abs(max(abs(df.basis_vectors(0) - df2.basis_vectors(0)))) < 1e-8);
        DLIB_TEST(std::abs(abs(df.b - df2.b)) < 1e-8);
    }

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

    class test_ranking_tools : public tester
    {
    public:
        test_ranking_tools (
        ) :
            tester ("test_ranking",
                    "Runs tests on the ranking tools.")
        {}

        void perform_test (
        )
        {
            test_count_ranking_inversions();
            dotest1();
            dotest_sparse_vectors();
            test_svmrank_weight_force_dense<true>();
            test_svmrank_weight_force_dense<false>();

        }
    } a;

}


//=======================================================================
// Copyright (c)
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================
/**
 * @file ir_steiner_tree_test.cpp
 * @brief
 * @author Maciej Andrejczuk, Piotr Godlewski, Piotr Wygocki
 * @version 1.0
 * @date 2013-02-04
 */
#include "test_utils/sample_graph.hpp"
#include "test_utils/logger.hpp"
#include "test_utils/test_result_check.hpp"

#include "paal/iterative_rounding/steiner_tree/steiner_tree.hpp"
#include "paal/utils/irange.hpp"

#include <boost/test/unit_test.hpp>

#include <cmath>

using Vertex = int;
using Terminals = std::vector<int>;

BOOST_AUTO_TEST_SUITE(ir_steiner_tree)

static const double APPROXIMATION_RATIO = 1.39;

BOOST_AUTO_TEST_CASE(test_all_generator) {
    paal::ir::steiner_tree_all_generator strategy_all(5);

    Terminals terminals, steiner_vertices;
    std::vector<Vertex> result;
    // small graph
    auto metrics = sample_graphs_metrics::get_graph_metric_steiner();
    boost::tie(terminals, steiner_vertices) =
        sample_graphs_metrics::get_graph_steiner_vertices();

    auto status = paal::ir::steiner_tree_iterative_rounding(metrics, terminals,
            steiner_vertices, std::back_inserter(result), strategy_all);
    BOOST_CHECK_EQUAL(status, paal::lp::OPTIMAL);
    int cost = paal::ir::steiner_utils::count_cost(result, terminals, metrics);
    BOOST_CHECK(cost == 4);

    // check if algorithm doesn't modify supplied data
    BOOST_CHECK(boost::equal(terminals,
        sample_graphs_metrics::get_graph_steiner_vertices().first));
    BOOST_CHECK(boost::equal(steiner_vertices,
        sample_graphs_metrics::get_graph_steiner_vertices().second));
    auto m2 = sample_graphs_metrics::get_graph_metric_steiner();
    // TODO function comparing 2 metrics
    int n = m2.size();
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            BOOST_CHECK(metrics(i, j) == m2(i, j));
        }
    }

    // bigger graph
    result.clear();
    metrics = sample_graphs_metrics::get_graph_metric_steiner_bigger();
    boost::tie(terminals, steiner_vertices) =
        sample_graphs_metrics::get_graph_steiner_bigger_vertices();

    status = paal::ir::steiner_tree_iterative_rounding(metrics, terminals,
            steiner_vertices, std::back_inserter(result), strategy_all);
    BOOST_CHECK_EQUAL(status, paal::lp::OPTIMAL);
    cost = paal::ir::steiner_utils::count_cost(result, terminals, metrics);
    BOOST_CHECK(cost == 15);
}

template <typename Strategy>
void run_multiple_seed_tests(Strategy& strategy) {
    Terminals terminals, steiner_vertices;
    std::vector<Vertex> result;

    // small graph
    auto metrics = sample_graphs_metrics::get_graph_metric_steiner();
    boost::tie(terminals, steiner_vertices) =
        sample_graphs_metrics::get_graph_steiner_vertices();

    int best_cost = std::numeric_limits<int>::max();
    for (unsigned long i : paal::irange(5)) {
        std::default_random_engine rng{i};
        LOGLN("small graph, seed " << i);
        result.clear();
        paal::ir::steiner_tree_ir_components<> comps{};
        comps.set<paal::ir::RoundCondition>(paal::ir::steiner_tree_round_condition{rng});
        auto status = paal::ir::steiner_tree_iterative_rounding(metrics, terminals,
                steiner_vertices, std::back_inserter(result), strategy, comps);
        BOOST_CHECK_EQUAL(status, paal::lp::OPTIMAL);
        int cost = paal::ir::steiner_utils::count_cost(result, terminals, metrics);
        paal::assign_min(best_cost, cost);
        BOOST_CHECK(cost >= 4);
    }
    // warning: randomized algorithm, approximation ratio could be violated
    check_result(best_cost, 4, APPROXIMATION_RATIO);

    // bigger graph
    metrics = sample_graphs_metrics::get_graph_metric_steiner_bigger();
    boost::tie(terminals, steiner_vertices) =
        sample_graphs_metrics::get_graph_steiner_bigger_vertices();

    best_cost = std::numeric_limits<int>::max();
    for (int i : paal::irange(5)) {
        LOGLN("big graph, seed " << i);
        srand(i);
        result.clear();
        auto status = paal::ir::steiner_tree_iterative_rounding(metrics, terminals,
                steiner_vertices, std::back_inserter(result), strategy);
        BOOST_CHECK_EQUAL(status, paal::lp::OPTIMAL);
        int cost = paal::ir::steiner_utils::count_cost(result, terminals, metrics);
        best_cost = std::min(best_cost, cost);
        BOOST_CHECK(cost >= 15);
    }
    // warning: randomized algorithm, approximation ratio could be violated
    check_result(best_cost, 15, APPROXIMATION_RATIO);
}

BOOST_AUTO_TEST_CASE(test_all_generator_seeds) {
    LOGLN("strategy_all");
    paal::ir::steiner_tree_all_generator strategy_all(5);
    run_multiple_seed_tests(strategy_all);
}

BOOST_AUTO_TEST_CASE(test_rand_generator) {
    LOGLN("strategy_rand");
    paal::ir::steiner_tree_random_generator strategy_rand(10, 5);
    run_multiple_seed_tests(strategy_rand);
}

BOOST_AUTO_TEST_CASE(test_smart_generator) {
    LOGLN("strategy_smart");
    paal::ir::steiner_tree_smart_generator strategy_smart(10, 5);
    run_multiple_seed_tests(strategy_smart);
}

BOOST_AUTO_TEST_CASE(test_graph_all_generator) {
    LOGLN("strategy_graph_all");
    srand(0);

    Terminals terminals, steiner_vertices;
    std::vector<Vertex> result;

    // small graph
    auto small_graph = sample_graphs_metrics::get_graph_steiner();
    auto metrics = sample_graphs_metrics::get_graph_metric_steiner();
    boost::tie(terminals, steiner_vertices) =
        sample_graphs_metrics::get_graph_steiner_vertices();

    int best_cost = std::numeric_limits<int>::max();
    for (int i : paal::irange(5)) {
        auto strategy = paal::ir::make_steiner_tree_graph_all_generator<Vertex>(
            small_graph, terminals, 5);
        srand(i);
        LOGLN("small graph, seed " << i);
        result.clear();
        auto status = paal::ir::steiner_tree_iterative_rounding(metrics, terminals,
                steiner_vertices, std::back_inserter(result), strategy);
        BOOST_CHECK_EQUAL(status, paal::lp::OPTIMAL);
        int cost = paal::ir::steiner_utils::count_cost(result, terminals, metrics);
        best_cost = std::min(best_cost, cost);
        BOOST_CHECK(cost >= 4);
    }
    // warning: randomized algorithm, approximation ratio could be violated
    check_result(best_cost, 4, APPROXIMATION_RATIO);

    // bigger graph
    auto bigger_graph = sample_graphs_metrics::get_graph_steiner_bigger();
    metrics = sample_graphs_metrics::get_graph_metric_steiner_bigger();
    boost::tie(terminals, steiner_vertices) =
        sample_graphs_metrics::get_graph_steiner_bigger_vertices();

    best_cost = std::numeric_limits<int>::max();
    for (int i : paal::irange(5)) {
        auto strategy = paal::ir::make_steiner_tree_graph_all_generator<Vertex>(
            bigger_graph, terminals, 5);
        LOGLN("big graph, seed " << i);
        srand(i);
        result.clear();
        auto status = paal::ir::steiner_tree_iterative_rounding(metrics, terminals,
                steiner_vertices, std::back_inserter(result), strategy);
        BOOST_CHECK_EQUAL(status, paal::lp::OPTIMAL);
        int cost = paal::ir::steiner_utils::count_cost(result, terminals, metrics);
        best_cost = std::min(best_cost, cost);
        BOOST_CHECK(cost >= 15);
    }
    // warning: randomized algorithm, approximation ratio could be violated
    check_result(best_cost, 15, APPROXIMATION_RATIO);
}

BOOST_AUTO_TEST_CASE(euclidean_metric_test) {
    using Points = std::vector<std::pair<int, int>>;

    srand(0);
    paal::ir::steiner_tree_random_generator strategy_rand(10, 5);
    paal::data_structures::euclidean_metric<int> em;
    Points terminals, steiner_vertices, result;

    std::tie(em, terminals, steiner_vertices) =
        sample_graphs_metrics::get_euclidean_steiner_sample();

    auto status = paal::ir::steiner_tree_iterative_rounding(em, terminals, steiner_vertices,
                std::back_inserter(result), strategy_rand);
    BOOST_CHECK_EQUAL(status, paal::lp::OPTIMAL);
    auto cost = paal::ir::steiner_utils::count_cost(result, terminals, em);

    BOOST_CHECK_EQUAL(result.size(), std::size_t(1));
    BOOST_CHECK(result.front() == std::make_pair(1, 1));
    BOOST_CHECK_CLOSE(cost, 4 * std::sqrt(2), 1e-6);
}

BOOST_AUTO_TEST_SUITE_END()


// Copyright Paul A. Bristow 2012.
// Copyright John Maddock 2012.
// Copyright Benjamin Sobotta 2012

// 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)

#ifdef _MSC_VER
#  pragma warning (disable : 4127) // conditional expression is constant.
#  pragma warning (disable : 4305) // 'initializing' : truncation from 'double' to 'const float'.
#  pragma warning (disable : 4310) // cast truncates constant value.
#  pragma warning (disable : 4512) // assignment operator could not be generated.
#endif

//#include <pch.hpp> // include directory libs/math/src/tr1/ is needed.

#include <boost/math/concepts/real_concept.hpp> // for real_concept
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp> // Boost.Test
#include <boost/test/floating_point_comparison.hpp>

#include <boost/math/distributions/skew_normal.hpp>
using boost::math::skew_normal_distribution;
using boost::math::skew_normal;
#include <boost/math/tools/test.hpp>

#include <iostream>
#include <iomanip>
using std::cout;
using std::endl;
using std::setprecision;
#include <limits>
using std::numeric_limits;
#include "test_out_of_range.hpp"

template <class RealType>
void check_skew_normal(RealType mean, RealType scale, RealType shape, RealType x, RealType p, RealType q, RealType tol)
{
 using boost::math::skew_normal_distribution;

  BOOST_CHECK_CLOSE_FRACTION(
    ::boost::math::cdf(   // Check cdf
    skew_normal_distribution<RealType>(mean, scale, shape),      // distribution.
    x),    // random variable.
    p,     // probability.
    tol);   // tolerance.
  BOOST_CHECK_CLOSE_FRACTION(
    ::boost::math::cdf( // Check cdf complement
    complement(
    skew_normal_distribution<RealType>(mean, scale, shape),   // distribution.
    x)),   // random variable.
    q,      // probability complement.
    tol);    // %tolerance.
  BOOST_CHECK_CLOSE_FRACTION(
    ::boost::math::quantile( // Check quantile
    skew_normal_distribution<RealType>(mean, scale, shape),    // distribution.
    p),   // probability.
    x,   // random variable.
    tol);   // tolerance.
  BOOST_CHECK_CLOSE_FRACTION(
    ::boost::math::quantile( // Check quantile complement
    complement(
    skew_normal_distribution<RealType>(mean, scale, shape),   // distribution.
    q)),   // probability complement.
    x,     // random variable.
    tol);  // tolerance.

   skew_normal_distribution<RealType> dist (mean, scale, shape);

   if((p < 0.999) && (q < 0.999))
   {  // We can only check this if P is not too close to 1,
      // so that we can guarantee Q is accurate:
      BOOST_CHECK_CLOSE_FRACTION(
        cdf(complement(dist, x)), q, tol); // 1 - cdf
      BOOST_CHECK_CLOSE_FRACTION(
        quantile(dist, p), x, tol); // quantile(cdf) = x
      BOOST_CHECK_CLOSE_FRACTION(
        quantile(complement(dist, q)), x, tol); // quantile(complement(1 - cdf)) = x
   }
} // template <class RealType>void check_skew_normal()

template <class RealType>
void test_spots(RealType)
{
   // Basic sanity checks
   RealType tolerance = 1e-4f; // 1e-4 (as %)

  // Check some bad parameters to the distribution,
#ifndef BOOST_NO_EXCEPTIONS
   BOOST_MATH_CHECK_THROW(boost::math::skew_normal_distribution<RealType> nbad1(0, 0), std::domain_error); // zero sd
   BOOST_MATH_CHECK_THROW(boost::math::skew_normal_distribution<RealType> nbad1(0, -1), std::domain_error); // negative sd
#else
   BOOST_MATH_CHECK_THROW(boost::math::skew_normal_distribution<RealType>(0, 0), std::domain_error); // zero sd
   BOOST_MATH_CHECK_THROW(boost::math::skew_normal_distribution<RealType>(0, -1), std::domain_error); // negative sd
#endif
  // Tests on extreme values of random variate x, if has numeric_limit infinity etc.
    skew_normal_distribution<RealType> N01;
  if(std::numeric_limits<RealType>::has_infinity)
  {
    BOOST_CHECK_EQUAL(pdf(N01, +std::numeric_limits<RealType>::infinity()), 0); // x = + infinity, pdf = 0
    BOOST_CHECK_EQUAL(pdf(N01, -std::numeric_limits<RealType>::infinity()), 0); // x = - infinity, pdf = 0
    BOOST_CHECK_EQUAL(cdf(N01, +std::numeric_limits<RealType>::infinity()), 1); // x = + infinity, cdf = 1
    BOOST_CHECK_EQUAL(cdf(N01, -std::numeric_limits<RealType>::infinity()), 0); // x = - infinity, cdf = 0
    BOOST_CHECK_EQUAL(cdf(complement(N01, +std::numeric_limits<RealType>::infinity())), 0); // x = + infinity, c cdf = 0
    BOOST_CHECK_EQUAL(cdf(complement(N01, -std::numeric_limits<RealType>::infinity())), 1); // x = - infinity, c cdf = 1
#ifndef BOOST_NO_EXCEPTIONS
    BOOST_MATH_CHECK_THROW(boost::math::skew_normal_distribution<RealType> nbad1(std::numeric_limits<RealType>::infinity(), static_cast<RealType>(1)), std::domain_error); // +infinite mean
    BOOST_MATH_CHECK_THROW(boost::math::skew_normal_distribution<RealType> nbad1(-std::numeric_limits<RealType>::infinity(),  static_cast<RealType>(1)), std::domain_error); // -infinite mean
    BOOST_MATH_CHECK_THROW(boost::math::skew_normal_distribution<RealType> nbad1(static_cast<RealType>(0), std::numeric_limits<RealType>::infinity()), std::domain_error); // infinite sd
#else
    BOOST_MATH_CHECK_THROW(boost::math::skew_normal_distribution<RealType>(std::numeric_limits<RealType>::infinity(), static_cast<RealType>(1)), std::domain_error); // +infinite mean
    BOOST_MATH_CHECK_THROW(boost::math::skew_normal_distribution<RealType>(-std::numeric_limits<RealType>::infinity(),  static_cast<RealType>(1)), std::domain_error); // -infinite mean
    BOOST_MATH_CHECK_THROW(boost::math::skew_normal_distribution<RealType>(static_cast<RealType>(0), std::numeric_limits<RealType>::infinity()), std::domain_error); // infinite sd
#endif
  }

  if (std::numeric_limits<RealType>::has_quiet_NaN)
  {
    // No longer allow x to be NaN, then these tests should throw.
    BOOST_MATH_CHECK_THROW(pdf(N01, +std::numeric_limits<RealType>::quiet_NaN()), std::domain_error); // x = NaN
    BOOST_MATH_CHECK_THROW(cdf(N01, +std::numeric_limits<RealType>::quiet_NaN()), std::domain_error); // x = NaN
    BOOST_MATH_CHECK_THROW(cdf(complement(N01, +std::numeric_limits<RealType>::quiet_NaN())), std::domain_error); // x = + infinity
    BOOST_MATH_CHECK_THROW(quantile(N01, +std::numeric_limits<RealType>::quiet_NaN()), std::domain_error); // p = + infinity
    BOOST_MATH_CHECK_THROW(quantile(complement(N01, +std::numeric_limits<RealType>::quiet_NaN())), std::domain_error); // p = + infinity
  }

   cout << "Tolerance for type " << typeid(RealType).name()  << " is " << tolerance << " %" << endl;

   // Tests where shape = 0, so same as normal tests.
   // (These might be removed later).
   check_skew_normal(
      static_cast<RealType>(5),
      static_cast<RealType>(2),
      static_cast<RealType>(0),
      static_cast<RealType>(4.8),
      static_cast<RealType>(0.46017),
      static_cast<RealType>(1 - 0.46017),
      tolerance);

   check_skew_normal(
      static_cast<RealType>(5),
      static_cast<RealType>(2),
      static_cast<RealType>(0),
      static_cast<RealType>(5.2),
      static_cast<RealType>(1 - 0.46017),
      static_cast<RealType>(0.46017),
      tolerance);

   check_skew_normal(
      static_cast<RealType>(5),
      static_cast<RealType>(2),
      static_cast<RealType>(0),
      static_cast<RealType>(2.2),
      static_cast<RealType>(0.08076),
      static_cast<RealType>(1 - 0.08076),
      tolerance);

   check_skew_normal(
      static_cast<RealType>(5),
      static_cast<RealType>(2),
      static_cast<RealType>(0),
      static_cast<RealType>(7.8),
      static_cast<RealType>(1 - 0.08076),
      static_cast<RealType>(0.08076),
      tolerance);

   check_skew_normal(
      static_cast<RealType>(-3),
      static_cast<RealType>(5),
      static_cast<RealType>(0),
      static_cast<RealType>(-4.5),
      static_cast<RealType>(0.38209),
      static_cast<RealType>(1 - 0.38209),
      tolerance);

   check_skew_normal(
      static_cast<RealType>(-3),
      static_cast<RealType>(5),
      static_cast<RealType>(0),
      static_cast<RealType>(-1.5),
      static_cast<RealType>(1 - 0.38209),
      static_cast<RealType>(0.38209),
      tolerance);

   check_skew_normal(
      static_cast<RealType>(-3),
      static_cast<RealType>(5),
      static_cast<RealType>(0),
      static_cast<RealType>(-8.5),
      static_cast<RealType>(0.13567),
      static_cast<RealType>(1 - 0.13567),
      tolerance);

   check_skew_normal(
      static_cast<RealType>(-3),
      static_cast<RealType>(5),
      static_cast<RealType>(0),
      static_cast<RealType>(2.5),
      static_cast<RealType>(1 - 0.13567),
      static_cast<RealType>(0.13567),
      tolerance);

   // Tests where shape != 0, specific to skew_normal distribution.
   //void check_skew_normal(RealType mean, RealType scale, RealType shape, RealType x, RealType p, RealType q, RealType tol)
      check_skew_normal( // 1st R example.
      static_cast<RealType>(1.1),
      static_cast<RealType>(2.2),
      static_cast<RealType>(-3.3),
      static_cast<RealType>(0.4), // x
      static_cast<RealType>(0.733918618927874), // p == psn
      static_cast<RealType>(1 - 0.733918618927874), // q
      tolerance);

   // Not sure about these yet.
      //check_skew_normal( // 2nd R example.
      //static_cast<RealType>(1.1),
      //static_cast<RealType>(0.02),
      //static_cast<RealType>(0.03),
      //static_cast<RealType>(1.3), // x
      //static_cast<RealType>(0.01), // p
      //static_cast<RealType>(0.09), // q
      //tolerance);
      //check_skew_normal( // 3nd R example.
      //static_cast<RealType>(10.1),
      //static_cast<RealType>(5.),
      //static_cast<RealType>(-0.03),
      //static_cast<RealType>(-1.3), // x
      //static_cast<RealType>(0.01201290665838824), // p
      //static_cast<RealType>(1. - 0.01201290665838824), // q 0.987987101
      //tolerance);

    // Tests for PDF: we know that the normal peak value is at 1/sqrt(2*pi)
   //
   tolerance = boost::math::tools::epsilon<RealType>() * 5; // 5 eps as a fraction
   BOOST_CHECK_CLOSE_FRACTION(
      pdf(skew_normal_distribution<RealType>(), static_cast<RealType>(0)),
      static_cast<RealType>(0.3989422804014326779399460599343818684759L), // 1/sqrt(2*pi)
      tolerance);
   BOOST_CHECK_CLOSE_FRACTION(
      pdf(skew_normal_distribution<RealType>(3), static_cast<RealType>(3)),
      static_cast<RealType>(0.3989422804014326779399460599343818684759L),
      tolerance);
   BOOST_CHECK_CLOSE_FRACTION(
      pdf(skew_normal_distribution<RealType>(3, 5), static_cast<RealType>(3)),
      static_cast<RealType>(0.3989422804014326779399460599343818684759L / 5),
      tolerance);

   // Shape != 0.
   BOOST_CHECK_CLOSE_FRACTION(
      pdf(skew_normal_distribution<RealType>(3,5,1e-6), static_cast<RealType>(3)),
      static_cast<RealType>(0.3989422804014326779399460599343818684759L / 5),
      tolerance);

   // Checks on mean, variance cumulants etc.
   // Checks on shape ==0

    RealType tol5 = boost::math::tools::epsilon<RealType>() * 5;
    skew_normal_distribution<RealType> dist(8, 3);
    RealType x = static_cast<RealType>(0.125);

    BOOST_MATH_STD_USING // ADL of std math lib names

    // mean:
    BOOST_CHECK_CLOSE(
       mean(dist)
       , static_cast<RealType>(8), tol5);
    // variance:
    BOOST_CHECK_CLOSE(
       variance(dist)
       , static_cast<RealType>(9), tol5);
    // std deviation:
    BOOST_CHECK_CLOSE(
       standard_deviation(dist)
       , static_cast<RealType>(3), tol5);
    // hazard:
    BOOST_CHECK_CLOSE(
       hazard(dist, x)
       , pdf(dist, x) / cdf(complement(dist, x)), tol5);
    // cumulative hazard:
    BOOST_CHECK_CLOSE(
       chf(dist, x)
       , -log(cdf(complement(dist, x))), tol5);
    // coefficient_of_variation:
    BOOST_CHECK_CLOSE(
       coefficient_of_variation(dist)
       , standard_deviation(dist) / mean(dist), tol5);
    // mode:
    BOOST_CHECK_CLOSE_FRACTION(mode(dist), static_cast<RealType>(8), 0.001f);

    BOOST_CHECK_CLOSE(
       median(dist)
       , static_cast<RealType>(8), tol5);

    // skewness:
    BOOST_CHECK_CLOSE(
       skewness(dist)
       , static_cast<RealType>(0), tol5);
    // kurtosis:
    BOOST_CHECK_CLOSE(
       kurtosis(dist)
       , static_cast<RealType>(3), tol5);
    // kurtosis excess:
    BOOST_CHECK_CLOSE(
       kurtosis_excess(dist)
       , static_cast<RealType>(0), tol5);

    skew_normal_distribution<RealType> norm01(0, 1); // Test default (0, 1)
    BOOST_CHECK_CLOSE(
       mean(norm01),
       static_cast<RealType>(0), 0); // Mean == zero

    skew_normal_distribution<RealType> defsd_norm01(0); // Test default (0, sd = 1)
    BOOST_CHECK_CLOSE(
       mean(defsd_norm01),
       static_cast<RealType>(0), 0); // Mean == zero

    skew_normal_distribution<RealType> def_norm01; // Test default (0, sd = 1)
    BOOST_CHECK_CLOSE(
       mean(def_norm01),
       static_cast<RealType>(0), 0); // Mean == zero

    BOOST_CHECK_CLOSE(
       standard_deviation(def_norm01),
       static_cast<RealType>(1), 0);  //

    BOOST_CHECK_CLOSE(
       mode(def_norm01),
       static_cast<RealType>(0), 0); // Mode == zero

    // Skew_normal tests with shape != 0.
    {
      // Note these tolerances are expressed as percentages, hence the extra * 100 on the end:
      RealType tol10 = boost::math::tools::epsilon<RealType>() * 10 * 100;
      RealType tol100 = boost::math::tools::epsilon<RealType>() * 100 * 100;

      //skew_normal_distribution<RealType> dist(1.1, 0.02, 0.03);

      BOOST_MATH_STD_USING // ADL of std math lib names.

      // Test values from R = see skew_normal_drv.cpp which included the R code used.
      {
        dist = skew_normal_distribution<RealType>(static_cast<RealType>(1.1l), static_cast<RealType>(2.2l), static_cast<RealType>(-3.3l));

        BOOST_CHECK_CLOSE(      // mean:
           mean(dist)
           , static_cast<RealType>(-0.579908992539856825862549L), tol10 * 2);

        std::cout << std::setprecision(17) << "Variance = " << variance(dist) << std::endl;
         BOOST_CHECK_CLOSE(      // variance: N[variance[skewnormaldistribution[1.1, 2.2, -3.3]], 50]
          variance(dist)
          , static_cast<RealType>(2.0179057767837232633904061072049998357047989154484L), tol10);

        BOOST_CHECK_CLOSE(      // skewness:
           skewness(dist)
           , static_cast<RealType>(-0.709854548171537509192897824663L), tol100);
        BOOST_CHECK_CLOSE(      // kurtosis:
           kurtosis(dist)
           , static_cast<RealType>(3.5538752625241790601377L), tol100);
        BOOST_CHECK_CLOSE(      // kurtosis excess:
           kurtosis_excess(dist)
           , static_cast<RealType>(0.5538752625241790601377L), tol100);

        BOOST_CHECK_CLOSE(
          pdf(dist, static_cast<RealType>(0.4L)),
          static_cast<RealType>(0.294140110156599539564571L),
          tol10);

        BOOST_CHECK_CLOSE(
          cdf(dist, static_cast<RealType>(0.4L)),
          static_cast<RealType>(0.7339186189278737976326676452L),
          tol100);

        BOOST_CHECK_CLOSE(
          quantile(dist, static_cast<RealType>(0.3L)),
          static_cast<RealType>(-1.180104068086875314419247L),
          tol100);

      { // mode tests

           dist = skew_normal_distribution<RealType>(static_cast<RealType>(0.l), static_cast<RealType>(1.l), static_cast<RealType>(4.l));

       // cout << "pdf(dist, 0) = " << pdf(dist, 0) <<  ", pdf(dist, 0.45) = " << pdf(dist, 0.45) << endl;
       // BOOST_CHECK_CLOSE(mode(dist), boost::math::constants::root_two<RealType>() / 2, tol5);
        BOOST_CHECK_CLOSE(mode(dist), static_cast<RealType>(0.41697299497388863932L), tol100);
      }

      }
      {
        dist = skew_normal_distribution<RealType>(static_cast<RealType>(1.1l), static_cast<RealType>(0.02l), static_cast<RealType>(0.03l));

        BOOST_CHECK_CLOSE(      // mean:
           mean(dist)
           , static_cast<RealType>(1.1004785154529557886162L), tol10);
        BOOST_CHECK_CLOSE(      // variance:
          variance(dist)
           , static_cast<RealType>(0.00039977102296128251645L), tol10);

        BOOST_CHECK_CLOSE(      // skewness:
           skewness(dist)
           , static_cast<RealType>(5.8834811259890359782e-006L), tol100);
        BOOST_CHECK_CLOSE(      // kurtosis:
           kurtosis(dist)
           , static_cast<RealType>(3.L + 9.2903475812137800239002e-008L), tol100);
        BOOST_CHECK_CLOSE(      // kurtosis excess:
           kurtosis_excess(dist)
           , static_cast<RealType>(9.2903475812137800239002e-008L), tol100);
      }
      {
        dist = skew_normal_distribution<RealType>(static_cast<RealType>(10.1l), static_cast<RealType>(5.l), static_cast<RealType>(-0.03l));
        BOOST_CHECK_CLOSE(      // mean:
           mean(dist)
           , static_cast<RealType>(9.9803711367610528459485937L), tol10);
        BOOST_CHECK_CLOSE(      // variance:
          variance(dist)
           , static_cast<RealType>(24.98568893508015727823L), tol10);

        BOOST_CHECK_CLOSE(      // skewness:
           skewness(dist)
           , static_cast<RealType>(-5.8834811259890359782085e-006L), tol100);
        BOOST_CHECK_CLOSE(      // kurtosis:
           kurtosis(dist)
           , static_cast<RealType>(3.L + 9.2903475812137800239002e-008L), tol100);
        BOOST_CHECK_CLOSE(      // kurtosis excess:
           kurtosis_excess(dist)
           , static_cast<RealType>(9.2903475812137800239002e-008L), tol100);
      }
      {
        dist = skew_normal_distribution<RealType>(static_cast<RealType>(-10.1l), static_cast<RealType>(5.l), static_cast<RealType>(30.l));
        BOOST_CHECK_CLOSE(      // mean:
           mean(dist)
           , static_cast<RealType>(-6.11279169674138408531365L), 2 * tol10);
        BOOST_CHECK_CLOSE(      // variance:
          variance(dist)
          , static_cast<RealType>(9.10216994642554914628242L), tol10 * 2);

        BOOST_CHECK_CLOSE(      // skewness:
           skewness(dist)
           , static_cast<RealType>(0.99072425443686904424L), tol100);
        BOOST_CHECK_CLOSE(      // kurtosis:
           kurtosis(dist)
           , static_cast<RealType>(3.L + 0.8638862008406084244563L), tol100);
        BOOST_CHECK_CLOSE(      // kurtosis excess:
           kurtosis_excess(dist)
           , static_cast<RealType>(0.8638862008406084244563L), tol100);
      }

      BOOST_MATH_CHECK_THROW(cdf(skew_normal_distribution<RealType>(0, 0, 0), 0), std::domain_error);
      BOOST_MATH_CHECK_THROW(cdf(skew_normal_distribution<RealType>(0, -1, 0), 0), std::domain_error);
      BOOST_MATH_CHECK_THROW(quantile(skew_normal_distribution<RealType>(0, 1, 0), -1), std::domain_error);
      BOOST_MATH_CHECK_THROW(quantile(skew_normal_distribution<RealType>(0, 1, 0), 2), std::domain_error);
      check_out_of_range<skew_normal_distribution<RealType> >(1, 1, 1);
    }

} // template <class RealType>void test_spots(RealType)

BOOST_AUTO_TEST_CASE( test_main )
{

  using boost::math::skew_normal;
  using boost::math::skew_normal_distribution;

  //int precision = 17; // std::numeric_limits<double::max_digits10;
  double tolfeweps = numeric_limits<double>::epsilon() * 5;
  //double tol6decdigits = numeric_limits<float>::epsilon() * 2;
  // Check that can generate skew_normal distribution using the two convenience methods:
  boost::math::skew_normal w12(1., 2); // Using typedef.
  boost::math::skew_normal_distribution<> w01; // Use default unity values for mean and scale.
  // Note NOT myn01() as the compiler will interpret as a function!

  // Checks on constructors.
  // Default parameters.
  BOOST_CHECK_EQUAL(w01.location(), 0);
  BOOST_CHECK_EQUAL(w01.scale(), 1);
  BOOST_CHECK_EQUAL(w01.shape(), 0);

  skew_normal_distribution<> w23(2., 3); // Using default RealType double.
  BOOST_CHECK_EQUAL(w23.scale(), 3);
  BOOST_CHECK_EQUAL(w23.shape(), 0);

  skew_normal_distribution<> w123(1., 2., 3.); // Using default RealType double.
  BOOST_CHECK_EQUAL(w123.location(), 1.);
  BOOST_CHECK_EQUAL(w123.scale(), 2.);
  BOOST_CHECK_EQUAL(w123.shape(), 3.);

  BOOST_CHECK_CLOSE_FRACTION(mean(w01), static_cast<double>(0), tolfeweps); // Default mean == zero
  BOOST_CHECK_CLOSE_FRACTION(scale(w01), static_cast<double>(1), tolfeweps); // Default scale == unity

  // Basic sanity-check spot values for all floating-point types..
  // (Parameter value, arbitrarily zero, only communicates the floating point type).
  test_spots(0.0F); // Test float. OK at decdigits = 0 tolerance = 0.0001 %
  test_spots(0.0); // Test double. OK at decdigits 7, tolerance = 1e07 %
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
  test_spots(0.0L); // Test long double.
#ifndef BOOST_MATH_NO_REAL_CONCEPT_TESTS
  test_spots(boost::math::concepts::real_concept(0.)); // Test real concept.
#endif
#else
  std::cout << "<note>The long double tests have been disabled on this platform "
    "either because the long double overloads of the usual math functions are "
    "not available at all, or because they are too inaccurate for these tests "
    "to pass.</note>" << std::endl;
#endif
  /*      */

} // BOOST_AUTO_TEST_CASE( test_main )

/*

Output:

*/


/**
 * @file electrostaticforce.cc
 * @brief ElectrostaticForce
 * @author Erick Schulz
 * @date 27.11.2019
 * @copyright Developed at ETH Zurich
 */

// HACK:
#undef SOLUTION
#define SOLUTION 1

#include "../electrostaticforce.h"

#include <gtest/gtest.h>

#include <Eigen/Core>

namespace ElectrostaticForce::test {

TEST(ElectrostaticForce, computeExactForce) {
  Eigen::Vector2d exactForce = ElectrostaticForce::computeExactForce();
  double tol = 1.0e-3;
  ASSERT_NEAR(13.0776, exactForce(0), tol);
  ASSERT_NEAR(0.0, exactForce(1), tol);
}

TEST(ElectrostaticForce, computeForceDomainFunctional) {
  std::string mesh_file =
      CURRENT_SOURCE_DIR "/../../meshes/emforce" + std::to_string(4) + ".msh";
  auto mesh_factory = std::make_unique<lf::mesh::hybrid2d::MeshFactory>(2);
  const lf::io::GmshReader reader(std::move(mesh_factory), mesh_file);
  auto mesh_p = reader.mesh();
  auto fe_space_p =
      std::make_shared<lf::uscalfe::FeSpaceLagrangeO1<double>>(mesh_p);

  Eigen::VectorXd approx_sol = ElectrostaticForce::solvePoissonBVP(fe_space_p);

  Eigen::Vector2d approx_force_domain_functional =
      ElectrostaticForce::computeForceDomainFunctional(fe_space_p, approx_sol);

  double tol = 1.0e-4;
  ASSERT_NEAR(13.0711, approx_force_domain_functional(0), tol);
  ASSERT_NEAR(0.01106, approx_force_domain_functional(1), tol);
}

TEST(ElectrostaticForce, computeForceBoundaryFunctional) {
  std::string mesh_file =
      CURRENT_SOURCE_DIR "/../../meshes/emforce" + std::to_string(4) + ".msh";
  auto mesh_factory = std::make_unique<lf::mesh::hybrid2d::MeshFactory>(2);
  const lf::io::GmshReader reader(std::move(mesh_factory), mesh_file);
  auto mesh_p = reader.mesh();
  auto fe_space_p =
      std::make_shared<lf::uscalfe::FeSpaceLagrangeO1<double>>(mesh_p);

  Eigen::VectorXd approx_sol = ElectrostaticForce::solvePoissonBVP(fe_space_p);

  Eigen::Vector2d approx_force_boundary_functional =
      ElectrostaticForce::computeForceBoundaryFunctional(fe_space_p,
                                                         approx_sol);

  double tol = 1.0e-3;
  ASSERT_NEAR(12.5894, approx_force_boundary_functional(0), tol);
  ASSERT_NEAR(-0.0602, approx_force_boundary_functional(1), tol);
}

TEST(ElectrostaticForce, solvePoissonBVPBoundaryConditions) {
  double tol = 1.0e-14;
  std::string mesh_file =
      CURRENT_SOURCE_DIR "/../../meshes/emforce" + std::to_string(1) + ".msh";
  auto mesh_factory = std::make_unique<lf::mesh::hybrid2d::MeshFactory>(2);
  const lf::io::GmshReader reader(std::move(mesh_factory), mesh_file);
  auto mesh_p = reader.mesh();
  auto fe_space_p =
      std::make_shared<lf::uscalfe::FeSpaceLagrangeO1<double>>(mesh_p);
  const lf::assemble::DofHandler &dofh{fe_space_p->LocGlobMap()};

  Eigen::VectorXd approx_sol = ElectrostaticForce::solvePoissonBVP(fe_space_p);

  auto bd_flags{lf::mesh::utils::flagEntitiesOnBoundary(mesh_p, 2)};
  for (const lf::mesh::Entity *node : mesh_p->Entities(2)) {
    if (bd_flags(*node)) {
      auto dof_idx = dofh.GlobalDofIndices(*node);
      auto endpoints = lf::geometry::Corners(*(node->Geometry()));
      if (endpoints.norm() < 0.27) {
        ASSERT_NEAR(1.0, approx_sol(dof_idx[0]), tol);
      } else {
        ASSERT_NEAR(0.0, approx_sol(dof_idx[0]), tol);
      }
    }
  }
}

}  // namespace ElectrostaticForce::test


#include <cassert>
#include <iostream>
#include <sstream>
#include <boost/program_options/variables_map.hpp>
#include "optimize.h"
#include "online_optimizer.h"
#include "sparse_vector.h"
#include "fdict.h"

using namespace std;

double TestOptimizer(BatchOptimizer* opt) {
  cerr << "TESTING NON-PERSISTENT OPTIMIZER\n";

  // f(x,y) = 4x1^2 + x1*x2 + x2^2 + x3^2 + 6x3 + 5
  // df/dx1 = 8*x1 + x2
  // df/dx2 = 2*x2 + x1
  // df/dx3 = 2*x3 + 6
  vector<double> x(3);
  vector<double> g(3);
  x[0] = 8;
  x[1] = 8;
  x[2] = 8;
  double obj = 0;
  do {
    g[0] = 8 * x[0] + x[1];
    g[1] = 2 * x[1] + x[0];
    g[2] = 2 * x[2] + 6;
    obj = 4 * x[0]*x[0] + x[0] * x[1] + x[1]*x[1] + x[2]*x[2] + 6 * x[2] + 5;
    opt->Optimize(obj, g, &x);

    cerr << x[0] << " " << x[1] << " " << x[2] << endl;
    cerr << "   obj=" << obj << "\td/dx1=" << g[0] << " d/dx2=" << g[1] << " d/dx3=" << g[2] << endl;
  } while (!opt->HasConverged());
  return obj;
}

double TestPersistentOptimizer(BatchOptimizer* opt) {
  cerr << "\nTESTING PERSISTENT OPTIMIZER\n";
  // f(x,y) = 4x1^2 + x1*x2 + x2^2 + x3^2 + 6x3 + 5
  // df/dx1 = 8*x1 + x2
  // df/dx2 = 2*x2 + x1
  // df/dx3 = 2*x3 + 6
  vector<double> x(3);
  vector<double> g(3);
  x[0] = 8;
  x[1] = 8;
  x[2] = 8;
  double obj = 0;
  string state;
  bool converged = false;
  while (!converged) {
    g[0] = 8 * x[0] + x[1];
    g[1] = 2 * x[1] + x[0];
    g[2] = 2 * x[2] + 6;
    obj = 4 * x[0]*x[0] + x[0] * x[1] + x[1]*x[1] + x[2]*x[2] + 6 * x[2] + 5;

    {
      if (state.size() > 0) {
        istringstream is(state, ios::binary);
        opt->Load(&is);
      }
      opt->Optimize(obj, g, &x);
      ostringstream os(ios::binary); opt->Save(&os); state = os.str();

    }

    cerr << x[0] << " " << x[1] << " " << x[2] << endl;
    cerr << "   obj=" << obj << "\td/dx1=" << g[0] << " d/dx2=" << g[1] << " d/dx3=" << g[2] << endl;
    converged = opt->HasConverged();
    if (!converged) {
      // now screw up the state (should be undone by Load)
      obj += 2.0;
      g[1] = -g[2];
      vector<double> x2 = x;
      try {
        opt->Optimize(obj, g, &x2);
      } catch (...) { }
    }
  }
  return obj;
}

template <class O>
void TestOptimizerVariants(int num_vars) {
  O oa(num_vars);
  cerr << "-------------------------------------------------------------------------\n";
  cerr << "TESTING: " << oa.Name() << endl;
  double o1 = TestOptimizer(&oa);
  O ob(num_vars);
  double o2 = TestPersistentOptimizer(&ob);
  if (o1 != o2) {
    cerr << oa.Name() << " VARIANTS PERFORMED DIFFERENTLY!\n" << o1 << " vs. " << o2 << endl;
    exit(1);
  }
  cerr << oa.Name() << " SUCCESS\n";
}

using namespace std::tr1;

void TestOnline() {
  size_t N = 20;
  double C = 1.0;
  double eta0 = 0.2;
  shared_ptr<LearningRateSchedule> r(new ExponentialDecayLearningRate(N, eta0, 0.85));
  //shared_ptr<LearningRateSchedule> r(new StandardLearningRate(N, eta0));
  CumulativeL1OnlineOptimizer opt(r, N, C, std::vector<int>());
  assert(r->eta(10) < r->eta(1));
}

int main() {
  int n = 3;
  TestOptimizerVariants<LBFGSOptimizer>(n);
  TestOptimizerVariants<RPropOptimizer>(n);
  TestOnline();
  return 0;
}


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

// @sect3{Include files}

// 前面几个文件已经在前面的例子中讲过了，因此不再做进一步的评论。

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

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

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

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

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

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

#include <fstream>
#include <iostream>

// 我们将在牛顿迭代之间使用自适应网格细化技术。要做到这一点，我们需要能够在新的网格上使用解决方案，尽管它是在旧的网格上计算出来的。SolutionTransfer类将解决方案从旧网格转移到新网格。

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

// 然后，我们为这个程序打开一个命名空间，像以前的程序一样，将dealii命名空间中的所有东西导入其中。

namespace Step15
{
  using namespace dealii;
// @sect3{The MinimalSurfaceProblem class template}

// 类模板与  step-6  中的基本相同。 增加了三个内容。

// - 有两个解决方案向量，一个用于牛顿更新  $\delta u^n$  ，另一个用于当前迭代  $u^n$  。

// -  setup_system 函数需要一个参数，表示这是否是第一次被调用。不同的是，第一次我们需要分配自由度，并将 $u^n$ 的解向量设置为正确的大小。接下来的几次，该函数是在我们已经完成了这些步骤，作为细化 refine_mesh 中网格的一部分之后被调用的。

// - 然后我们还需要新的函数。  set_boundary_values() 负责正确设置解向量的边界值，这在介绍的最后已经讨论过了。  compute_residual() 是一个计算非线性（离散）残差规范的函数。我们用这个函数来监测牛顿迭代的收敛性。该函数以步长 $\alpha^n$ 为参数来计算 $u^n + \alpha^n \; \delta u^n$ 的残差。这是人们通常需要的步长控制，尽管我们在这里不会使用这个功能。最后， determine_step_length() 计算每个牛顿迭代中的步长 $\alpha^n$ 。正如介绍中所讨论的，我们在这里使用一个固定的步长，并把实现一个更好的策略作为一个练习。(  step-77 的做法不同。它只是在整个求解过程中使用了一个外部包，而一个好的直线搜索策略是该包所提供的一部分）。)

  template <int dim>
  class MinimalSurfaceProblem
  {
  public:
    MinimalSurfaceProblem();
    void run();

  private:
    void   setup_system(const bool initial_step);
    void   assemble_system();
    void   solve();
    void   refine_mesh();
    void   set_boundary_values();
    double compute_residual(const double alpha) const;
    double determine_step_length() const;
    void   output_results(const unsigned int refinement_cycle) const;

    Triangulation<dim> triangulation;

    DoFHandler<dim> dof_handler;
    FE_Q<dim>       fe;

    AffineConstraints<double> hanging_node_constraints;

    SparsityPattern      sparsity_pattern;
    SparseMatrix<double> system_matrix;

    Vector<double> current_solution;
    Vector<double> newton_update;
    Vector<double> system_rhs;
  };
// @sect3{Boundary condition}

// 边界条件的实现就像在  step-4  中一样。 它被选为  $g(x,y)=\sin(2 \pi (x+y))$  。

  template <int dim>
  class BoundaryValues : public Function<dim>
  {
  public:
    virtual double value(const Point<dim> & p,
                         const unsigned int component = 0) const override;
  };

  template <int dim>
  double BoundaryValues<dim>::value(const Point<dim> &p,
                                    const unsigned int /*component*/) const
  {
    return std::sin(2 * numbers::PI * (p[0] + p[1]));
  }
// @sect3{The MinimalSurfaceProblem class implementation}
// @sect4{MinimalSurfaceProblem::MinimalSurfaceProblem}

// 该类的构造函数和析构函数与前几篇教程中的相同。

  template <int dim>
  MinimalSurfaceProblem<dim>::MinimalSurfaceProblem()
    : dof_handler(triangulation)
    , fe(2)
  {}
// @sect4{MinimalSurfaceProblem::setup_system}

// 在setup-system函数中，我们总是设置有限元方法的变量。与 step-6 有相同的区别，因为在那里我们在每个细化周期中都要从头开始求解PDE，而在这里我们需要把以前的网格的解放到当前的网格上。因此，我们不能只是重置解向量。因此，传递给这个函数的参数表明我们是否可以分布自由度（加上计算约束）并将解向量设置为零，或者这在其他地方已经发生过了（特别是在 refine_mesh() ）。

  template <int dim>
  void MinimalSurfaceProblem<dim>::setup_system(const bool initial_step)
  {
    if (initial_step)
      {
        dof_handler.distribute_dofs(fe);
        current_solution.reinit(dof_handler.n_dofs());

        hanging_node_constraints.clear();
        DoFTools::make_hanging_node_constraints(dof_handler,
                                                hanging_node_constraints);
        hanging_node_constraints.close();
      }

// 该函数的其余部分与  step-6  中的相同。

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

    DynamicSparsityPattern dsp(dof_handler.n_dofs());
    DoFTools::make_sparsity_pattern(dof_handler, dsp);

    hanging_node_constraints.condense(dsp);

    sparsity_pattern.copy_from(dsp);
    system_matrix.reinit(sparsity_pattern);
  }
// @sect4{MinimalSurfaceProblem::assemble_system}

// 这个函数的作用与前面的教程相同，当然，现在矩阵和右手边的函数取决于上一次迭代的解。正如在介绍中所讨论的，我们需要使用牛顿更新的零边界值；我们在这个函数的最后计算它们。

// 该函数的顶部包含了通常的模板代码，设置了允许我们在正交点评估形状函数的对象，以及本地矩阵和向量的临时存储位置，以及正交点上先前解的梯度。然后我们开始在所有单元格上进行循环。

  template <int dim>
  void MinimalSurfaceProblem<dim>::assemble_system()
  {
    const QGauss<dim> quadrature_formula(fe.degree + 1);

    system_matrix = 0;
    system_rhs    = 0;

    FEValues<dim> fe_values(fe,
                            quadrature_formula,
                            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<double> cell_matrix(dofs_per_cell, dofs_per_cell);
    Vector<double>     cell_rhs(dofs_per_cell);

    std::vector<Tensor<1, dim>> old_solution_gradients(n_q_points);

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

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

        fe_values.reinit(cell);

// 为了组装线性系统，我们必须在正交点上获得前一个解的梯度值。有一个标准的方法： FEValues::get_function_gradients 函数接收一个代表定义在DoFHandler上的有限元场的向量，并评估这个场在FEValues对象最后被重新初始化的单元的正交点的梯度。然后将所有正交点的梯度值写入第二个参数中。

        fe_values.get_function_gradients(current_solution,
                                         old_solution_gradients);

// 有了这个，我们就可以对所有的正交点和形状函数进行积分循环。 在刚刚计算了正交点中旧解的梯度后，我们就可以计算这些点中的系数 $a_{n}$ 。 然后，系统本身的组装看起来与我们一贯的做法相似，除了非线性项之外，将结果从局部对象复制到全局对象中也是如此。

        for (unsigned int q = 0; q < n_q_points; ++q)
          {
            const double coeff =
              1.0 / std::sqrt(1 + old_solution_gradients[q] *
                                    old_solution_gradients[q]);

            for (unsigned int i = 0; i < dofs_per_cell; ++i)
              {
                for (unsigned int j = 0; j < dofs_per_cell; ++j)
                  cell_matrix(i, j) +=
                    (((fe_values.shape_grad(i, q)      // ((\nabla \phi_i
                       * coeff                         //   * a_n
                       * fe_values.shape_grad(j, q))   //   * \nabla \phi_j)
                      -                                //  -
                      (fe_values.shape_grad(i, q)      //  (\nabla \phi_i
                       * coeff * coeff * coeff         //   * a_n^3
                       * (fe_values.shape_grad(j, q)   //   * (\nabla \phi_j
                          * old_solution_gradients[q]) //      * \nabla u_n)
                       * old_solution_gradients[q]))   //   * \nabla u_n)))
                     * fe_values.JxW(q));              // * dx

                cell_rhs(i) -= (fe_values.shape_grad(i, q)  // \nabla \phi_i
                                * coeff                     // * a_n
                                * old_solution_gradients[q] // * u_n
                                * fe_values.JxW(q));        // * dx
              }
          }

        cell->get_dof_indices(local_dof_indices);
        for (unsigned int i = 0; i < dofs_per_cell; ++i)
          {
            for (unsigned int j = 0; j < dofs_per_cell; ++j)
              system_matrix.add(local_dof_indices[i],
                                local_dof_indices[j],
                                cell_matrix(i, j));

            system_rhs(local_dof_indices[i]) += cell_rhs(i);
          }
      }

// 最后，我们从系统中移除悬挂的节点，并将零边界值应用到定义牛顿更新的线性系统中  $\delta u^n$  。

    hanging_node_constraints.condense(system_matrix);
    hanging_node_constraints.condense(system_rhs);

    std::map<types::global_dof_index, double> boundary_values;
    VectorTools::interpolate_boundary_values(dof_handler,
                                             0,
                                             Functions::ZeroFunction<dim>(),
                                             boundary_values);
    MatrixTools::apply_boundary_values(boundary_values,
                                       system_matrix,
                                       newton_update,
                                       system_rhs);
  }

//  @sect4{MinimalSurfaceProblem::solve}

// 解算函数和以往一样。在求解过程的最后，我们通过设置 $u^{n+1}=u^n+\alpha^n\;\delta u^n$ 来更新当前的解决方案。

  template <int dim>
  void MinimalSurfaceProblem<dim>::solve()
  {
    SolverControl            solver_control(system_rhs.size(),
                                 system_rhs.l2_norm() * 1e-6);
    SolverCG<Vector<double>> solver(solver_control);

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

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

    hanging_node_constraints.distribute(newton_update);

    const double alpha = determine_step_length();
    current_solution.add(alpha, newton_update);
  }
// @sect4{MinimalSurfaceProblem::refine_mesh}

// 这个函数的第一部分与 step-6 中的内容相同 ... 然而，在细化网格后，我们必须将旧的解决方案转移到新的解决方案中，我们在SolutionTransfer类的帮助下完成。这个过程稍微有点复杂，所以让我们详细描述一下。

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

    KellyErrorEstimator<dim>::estimate(
      dof_handler,
      QGauss<dim - 1>(fe.degree + 1),
      std::map<types::boundary_id, const Function<dim> *>(),
      current_solution,
      estimated_error_per_cell);

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

// 然后我们需要一个额外的步骤：例如，如果你标记了一个比它的邻居更精炼一次的单元，而这个邻居没有被标记为精炼，我们最终会在一个单元界面上跳过两个精炼级别。 为了避免这些情况，库将默默地也要对邻居单元进行一次细化。它通过在实际进行细化和粗化之前调用 Triangulation::prepare_coarsening_and_refinement 函数来实现。 这个函数标志着一组额外的单元格进行细化或粗化，以执行像单悬节点规则这样的规则。 调用此函数后，被标记为细化和粗化的单元格正是那些将被实际细化或粗化的单元格。通常情况下，你不需要手工操作 (Triangulation::execute_coarsening_and_refinement 为你做这个）。) 然而，我们需要初始化SolutionTransfer类，它需要知道最终将被粗化或细化的单元集，以便存储旧网格的数据并转移到新网格。因此，我们手动调用这个函数。

    triangulation.prepare_coarsening_and_refinement();

// 有了这个方法，我们用现在的DoFHandler初始化一个SolutionTransfer对象，并将解决方案向量附加到它上面，然后在新网格上进行实际的细化和自由度分配

    SolutionTransfer<dim> solution_transfer(dof_handler);
    solution_transfer.prepare_for_coarsening_and_refinement(current_solution);

    triangulation.execute_coarsening_and_refinement();

    dof_handler.distribute_dofs(fe);

// 最后，我们找回插值到新网格的旧解。由于SolutionTransfer函数实际上并不存储旧的解决方案的值，而是索引，我们需要保留旧的解决方案向量，直到我们得到新的内插值。因此，我们将新的数值写入一个临时的向量中，之后才将其写入解决方案向量对象中。

    Vector<double> tmp(dof_handler.n_dofs());
    solution_transfer.interpolate(current_solution, tmp);
    current_solution = tmp;

// 在新的网格上，有不同的悬挂节点，对于这些节点，我们必须在扔掉之前的对象内容后，重新计算约束。为了安全起见，我们还应该确保当前解决方案的向量条目满足悬空节点的约束条件（参见SolutionTransfer类文档中的讨论，了解为什么必须这样做）。我们可以通过明确调用`hanging_node_constraints.distribution(current_solution)`来做到这一点；我们省略这一步，因为这将在下面调用`set_boundary_values()`的最后发生，而且没有必要做两次。

    hanging_node_constraints.clear();

    DoFTools::make_hanging_node_constraints(dof_handler,
                                            hanging_node_constraints);
    hanging_node_constraints.close();

// 一旦我们有了内插的解决方案和所有关于悬挂节点的信息，我们必须确保我们现在的 $u^n$ 实际上有正确的边界值。正如在介绍的最后所解释的，即使细化前的解决方案有正确的边界值，也不会自动出现这种情况，因此我们必须明确地确保它现在有。

    set_boundary_values();

// 我们通过更新所有剩余的数据结构来结束这个函数，向 setup_dofs() 表明这不是第一次了，它需要保留解向量的内容。

    setup_system(false);
  }

//  @sect4{MinimalSurfaceProblem::set_boundary_values}

// 下一个函数确保解向量的条目尊重我们问题的边界值。 在细化了网格之后（或者刚刚开始计算），边界上可能会出现新的节点。这些节点的数值是在`refine_mesh()`中从之前的网格中简单插值出来的，而不是正确的边界值。这个问题可以通过将当前解决方案向量的所有边界节点明确设置为正确的值来解决。

// 但是有一个问题我们必须注意：如果我们有一个挂起的节点紧挨着一个新的边界节点，那么它的值也必须被调整以确保有限元场保持连续。这就是这个函数最后一行的调用所做的。

  template <int dim>
  void MinimalSurfaceProblem<dim>::set_boundary_values()
  {
    std::map<types::global_dof_index, double> boundary_values;
    VectorTools::interpolate_boundary_values(dof_handler,
                                             0,
                                             BoundaryValues<dim>(),
                                             boundary_values);
    for (auto &boundary_value : boundary_values)
      current_solution(boundary_value.first) = boundary_value.second;

    hanging_node_constraints.distribute(current_solution);
  }
// @sect4{MinimalSurfaceProblem::compute_residual}

// 为了监测收敛性，我们需要一种方法来计算（离散）残差的规范，即在介绍中讨论的向量 $\left<F(u^n),\varphi_i\right>$ 与 $F(u)=-\nabla \cdot \left(\frac{1}{\sqrt{1+|\nabla u|^{2}}}\nabla u \right)$ 的规范。事实证明，（尽管我们在当前版本的程序中没有使用这个功能）在确定最佳步长时需要计算残差 $\left<F(u^n+\alpha^n\;\delta u^n),\varphi_i\right>$ ，因此这就是我们在这里实现的：该函数将步长 $\alpha^n$ 作为参数。原有的功能当然是通过传递一个零作为参数得到的。

// 在下面的函数中，我们首先为残差设置一个向量，然后为评估点设置一个向量  $u^n+\alpha^n\;\delta u^n$  。接下来是我们在所有的积分操作中使用的相同的模板代码。

  template <int dim>
  double MinimalSurfaceProblem<dim>::compute_residual(const double alpha) const
  {
    Vector<double> residual(dof_handler.n_dofs());

    Vector<double> evaluation_point(dof_handler.n_dofs());
    evaluation_point = current_solution;
    evaluation_point.add(alpha, newton_update);

    const QGauss<dim> quadrature_formula(fe.degree + 1);
    FEValues<dim>     fe_values(fe,
                            quadrature_formula,
                            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();

    Vector<double>              cell_residual(dofs_per_cell);
    std::vector<Tensor<1, dim>> gradients(n_q_points);

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

    for (const auto &cell : dof_handler.active_cell_iterators())
      {
        cell_residual = 0;
        fe_values.reinit(cell);

// 实际的计算与  assemble_system()  中的计算差不多。我们首先评估 $u^n+\alpha^n\,\delta u^n$ 在正交点的梯度，然后计算系数 $a_n$ ，然后将其全部插入残差公式中。

        fe_values.get_function_gradients(evaluation_point, gradients);

        for (unsigned int q = 0; q < n_q_points; ++q)
          {
            const double coeff =
              1. / std::sqrt(1 + gradients[q] * gradients[q]);

            for (unsigned int i = 0; i < dofs_per_cell; ++i)
              cell_residual(i) -= (fe_values.shape_grad(i, q) // \nabla \phi_i
                                   * coeff                    // * a_n
                                   * gradients[q]             // * u_n
                                   * fe_values.JxW(q));       // * dx
          }

        cell->get_dof_indices(local_dof_indices);
        for (unsigned int i = 0; i < dofs_per_cell; ++i)
          residual(local_dof_indices[i]) += cell_residual(i);
      }

// 在这个函数的最后，我们还必须处理悬挂节点的约束和边界值的问题。关于后者，我们必须将所有对应于位于边界的自由度的条目的残差向量元素设置为零。原因是，由于那里的解的值是固定的，它们当然不是 "真正的 "自由度，因此，严格来说，我们不应该在残差向量中为它们集合条目。然而，正如我们一直所做的那样，我们想在每个单元上做完全相同的事情，因此我们并不想在上面的积分中处理某个自由度是否位于边界的问题。相反，我们将简单地在事后将这些条目设置为零。为此，我们需要确定哪些自由度实际上属于边界，然后在所有这些自由度上进行循环，并将剩余条目设置为零。这发生在以下几行中，我们已经在 step-11 中看到了使用DoFTools命名空间的适当函数。

    hanging_node_constraints.condense(residual);

    for (types::global_dof_index i :
         DoFTools::extract_boundary_dofs(dof_handler))
      residual(i) = 0;

// 在函数的最后，我们返回残差的常数。

    return residual.l2_norm();
  }

//  @sect4{MinimalSurfaceProblem::determine_step_length}

// 正如介绍中所讨论的，如果我们总是采取全步，即计算 $u^{n+1}=u^n+\delta u^n$ ，牛顿方法经常不收敛。相反，我们需要一个阻尼参数（步长）  $\alpha^n$  并设置  $u^{n+1}=u^n+\alpha^n\delta u^n$  。这个函数是用来计算 $\alpha^n$  的。

// 在这里，我们简单地总是返回0.1。这当然是一个次优的选择：理想情况下，人们希望的是，当我们越来越接近解的时候，步长变成1，这样我们就可以享受牛顿方法的快速二次收敛。我们将在下面的结果部分讨论更好的策略， step-77 也涉及这方面的内容。

  template <int dim>
  double MinimalSurfaceProblem<dim>::determine_step_length() const
  {
    return 0.1;
  }

//  @sect4{MinimalSurfaceProblem::output_results}

// 从`run()`调用的最后一个函数以图形形式输出当前的解决方案（和牛顿更新），作为VTU文件。它与之前教程中使用的完全相同。

  template <int dim>
  void MinimalSurfaceProblem<dim>::output_results(
    const unsigned int refinement_cycle) const
  {
    DataOut<dim> data_out;

    data_out.attach_dof_handler(dof_handler);
    data_out.add_data_vector(current_solution, "solution");
    data_out.add_data_vector(newton_update, "update");
    data_out.build_patches();

    const std::string filename =
      "solution-" + Utilities::int_to_string(refinement_cycle, 2) + ".vtu";
    std::ofstream output(filename);
    data_out.write_vtu(output);
  }
// @sect4{MinimalSurfaceProblem::run}

// 在运行函数中，我们建立第一个网格，然后有牛顿迭代的顶层逻辑。

// 正如在介绍中所描述的，领域是围绕原点的单位圆盘，创建方式与 step-6 中所示相同。网格经过两次全局细化，然后再进行若干次适应性循环。

// 在开始牛顿循环之前，我们还需要做一些设置工作。我们需要创建基本的数据结构，并确保第一个牛顿迭代已经有了正确的边界值，这在介绍中已经讨论过了。

  template <int dim>
  void MinimalSurfaceProblem<dim>::run()
  {
    GridGenerator::hyper_ball(triangulation);
    triangulation.refine_global(2);

    setup_system(/*first time=*/true);
    set_boundary_values();

// 接下来开始牛顿迭代。我们一直迭代到上一次迭代结束时计算的残差（规范）小于 $10^{-3}$ ，正如在 "do{ ... } while "循环结束时的检查。因为我们没有一个合理的值来初始化这个变量，所以我们只是使用可以表示为`双数'的最大值。

    double       last_residual_norm = std::numeric_limits<double>::max();
    unsigned int refinement_cycle   = 0;
    do
      {
        std::cout << "Mesh refinement step " << refinement_cycle << std::endl;

        if (refinement_cycle != 0)
          refine_mesh();

// 在每个网格上，我们正好做五个牛顿步骤。我们在这里打印初始残差，然后在这个网格上开始迭代。

// 在每一个牛顿步骤中，首先要计算系统矩阵和右手边，然后我们存储右手边的规范作为残差，以便在决定是否停止迭代时进行检查。然后我们求解线性系统（该函数也会更新 $u^{n+1}=u^n+\alpha^n\;\delta u^n$ ），并在这个牛顿步骤结束时输出残差的准则。

// 在这个循环结束后，我们还将以图形形式输出当前网格上的解，并增加网格细化循环的计数器。

        std::cout << "  Initial residual: " << compute_residual(0) << std::endl;

        for (unsigned int inner_iteration = 0; inner_iteration < 5;
             ++inner_iteration)
          {
            assemble_system();
            last_residual_norm = system_rhs.l2_norm();

            solve();

            std::cout << "  Residual: " << compute_residual(0) << std::endl;
          }

        output_results(refinement_cycle);

        ++refinement_cycle;
        std::cout << std::endl;
      }
    while (last_residual_norm > 1e-3);
  }
} // namespace Step15
// @sect4{The main function}

// 最后是主函数。这遵循了所有其他主函数的方案。

int main()
{
  try
    {
      using namespace Step15;

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

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


///////////////////////////////////////////////////////////////////////////////
// example::iterator::flatten_iterator2.cpp                                  //
//                                                                           //
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)         //
///////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <boost/array.hpp>
#include <boost/multi_array.hpp>
#include <boost/assign/std/vector.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/range.hpp>
#include <vector>
#include <boost/iterator/flatten_iterator.hpp>
#include <boost/range/flatten_range.hpp>
#include <libs/iterator/example/flatten_iterator2.h>

void example_flatten_iterator2(std::ostream& out){
    out << "->example_flatten_iterator2 : ";

    using namespace boost;
    typedef int val_;
    typedef std::vector<val_>							vals_;
	typedef boost::multi_array<val_, 2> 				ma_;
	typedef boost::multi_array_types::index_range 		idx_range_;
	typedef boost::multi_array_types::index 			idx_;
    typedef boost::range_iterator<const ma_>::type		it_ma_;
    typedef flatten_iterator<it_ma_>					flat_it_;

    typedef boost::array_view_gen<ma_,2>::type 			view2_;
    typedef boost::range_iterator<view2_>::type 		nit2_;
    typedef flatten_iterator<nit2_>						flat_it2_;

    typedef boost::array_view_gen<ma_,1>::type 			view1_;

    //  	array_type::index_gen indices;
    //  	array_type::array_view<2>::type myview =
    //    myarray[ indices[range(0,2)][1][range(0,4,2)] ];

    ma_ ma(boost::extents[2][5]);
    {
        vals_ vals1;
    	using namespace boost::assign;
        vals1 += 1,2,3,4,5;
        vals1 += 6,7,8,9,10;
        vals1 += 11,12,13,14,15;
        ma.assign(boost::begin(vals1),boost::end(vals1));
	}

//    view1_ view1 = ma[ boost::indices[1][idx_range_(0,4)] ]; // idx_range_(0,2)
//    typedef boost::range_iterator<const view_>::type nit_;

    view2_ view2 = ma[ boost::indices[idx_range_()][idx_range_(0,4)] ]; // idx_range_(0,2)
//    typedef boost::range_iterator<view2_>::type nit2_;
//    nit2_ nit2 = boost::begin(view2);

	flat_it2_ it2_b(boost::begin(view2),boost::end(view2));
	flat_it2_ it2_e(boost::end(view2),boost::end(view2));
    std::copy(it2_b,it2_e,std::ostream_iterator<val_>(std::cout," "));

//	it_ma_ it_ma = boost::begin(ma);
//    flat_it_ b(ma.begin(),ma.end());

    //	ma[boost::indices[1][idx_range_(0,4)]] = vals1; //assign(boost::begin(vals1),boost::end(vals1));
    //	ma(boost::indices[1]);

    out << "<-" << std::endl;
}