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

  @copyright 2016 NumScale SAS

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

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{

 /*!

    @ingroup group-hyperbolic
    Function object implementing sinhc capabilities

    Returns hyperbolic cardinal sine: \f$\sinh(x)/x\f$.

    @par Semantic:

    For every parameter of floating type @c T

    @code
    T r = sinhc(x);
    @endcode

    @see sinh

  **/
  Value sinhc(Value const & v0);
} }
#endif

#include <boost/simd/function/scalar/sinhc.hpp>
#include <boost/simd/function/simd/sinhc.hpp>

#endif


#include <vector>
#include <Eigen/Dense>
#include "matplotlibcpp.h"
namespace plt = matplotlibcpp;

int main() {

  const unsigned n = 100;
  Eigen::MatrixXd A(n / 2, n);
  std::vector<std::vector<double>> B;

  for (unsigned i = 0; i < n / 2; ++i) {
    A(i, i) = 1;
    std::vector<double> row(n);
    row[i] = 1;

    if (i < n / 2) {
      A(i, i + n / 2) = 1;
      row[i + n / 2] = 1;
    }
    B.push_back(row);
  }

  for (unsigned i = 0; i < n / 2; ++i) {
    for (unsigned j = 0; j < n; ++j) {
      if (A(i, j) != B[i][j]) {
        std::cout << i << "," << j << " differ!\n";
      }
    }
  }

  plt::figure();
  plt::title("Eigen");
  plt::spy(A);

  plt::figure();
  plt::title("vector");
  plt::spy(B);
  plt::show();
  return 0;
}


#ifndef TDHH_ROUTER_HPP
#define TDHH_ROUTER_HPP

#include <string>
#include "PacketsReader.hpp"
#include "Hyperloglog.hpp"
#include "Heap.hpp"
#include <map>
#include <boost/functional/hash.hpp>
#include <boost/math/special_functions/beta.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include "Utils.hpp"

namespace TDHH {
    using namespace std;
    using namespace hll;

    const int ITERATIONS = 1;

    class Router {

    private:
        DATASET dataset;

    protected:
        string filename;
        PacketsReader pr;

    public:
        explicit Router(const string &filename, DATASET dataset) :
                filename(filename),
                pr(filename, dataset),
                dataset(dataset) {}

        void reset() {
            pr.reset();
        }

        map<int, map<int, vector<double>>> volumeEstimation(vector<int> counters) {
            map<int, map<int, vector<double>>> res;
            std::random_device rd;
            map<int, vector<HyperLogLog> > counter_to_hll_arr;
            for (const int c : counters) {
                auto bits = static_cast<int>(log2(c));
                vector<HyperLogLog> hll_arr;
                for (int I = 0; I < ITERATIONS; I++) {
                    HyperLogLog hll(static_cast<uint8_t>(bits));
                    hll.set_seed(rd());
                    hll_arr.push_back(hll);
                }
                counter_to_hll_arr.insert(pair<int, vector<HyperLogLog>>(c, hll_arr));
            }
            auto pkt = pr.getNextIPPacket();
            int num_pkts = 0;
            while (pkt != nullptr) {
                ++num_pkts;
                const auto &pkt_string = pkt->getReprString();
                for (const int c: counters) {
                    for (auto &hll : counter_to_hll_arr.at(c)) {
                        hll.add(pkt_string.c_str(), static_cast<int>(pkt_string.size()));
                    }
                }
                delete pkt;
                if (num_pkts % 1000000 == 0) {
                    for (const int c: counters) {
                        for (const auto &hll : counter_to_hll_arr.at(c)) {
                            res[c][num_pkts].push_back(hll.estimate());
                        }
                    }
                }
                pkt = pr.getNextIPPacket();
            }
            for (const int c: counters) {
                for (const auto &hll : counter_to_hll_arr.at(c)) {
                    res[c][num_pkts].push_back(hll.estimate());
                }
            }
            return res;
        }

        map<pair<double, double>, vector<map<string, double>>> heavy_hitters(vector<pair<double, double>> params) {
            map<pair<double, double>, vector<map<string, double>>> res;

            map<pair<double, double>, vector<Heap *>> param_to_heap_arr;
            for (const auto &param : params) {
                double eps = param.first;
                double delta = param.second;
                auto chi = static_cast<unsigned int>(ceil(9.0 / (eps * eps) * log2(2.0 / (delta * eps))));
                vector<Heap *> heap_arr;
                for (int I = 0; I < ITERATIONS; I++) {
                    auto *heap = new Heap(chi);
                    heap_arr.push_back(heap);
                }
                param_to_heap_arr.insert(pair<pair<double, double>, vector<Heap *>>(param, heap_arr));
            }

            unsigned int num_pkts = 0;
            auto pkt = pr.getNextIPPacket();
            while (pkt != nullptr) {
                for (const auto &param : params) {
                    for (auto &heap : param_to_heap_arr.at(param)) {
                        heap->Add(pkt->getReprString());
                    }
                }
                delete pkt;
                pkt = pr.getNextIPPacket();
                ++num_pkts;
            }

            for (const auto &param : params) {
                vector<map<string, double>> samples;
                for (auto &heap : param_to_heap_arr.at(param)) {
                    samples.push_back(heap->GetSample());
                }
                res.insert(pair<pair<double, double>, vector<map<string, double>>>(param, samples));
            }

            for (const auto &param : params) {
                for (auto &heap : param_to_heap_arr.at(param)) {
                    delete heap;
                }
            }

            return res;
        }

        int doNothing() {
            auto pkt = pr.getNextIPPacket();
            int num_pkts = 0;
            while (pkt != nullptr) {
                ++num_pkts;
                const auto &pkt_string = pkt->getReprString();
                delete pkt;
                pkt = pr.getNextIPPacket();
            }
            return num_pkts;
        }

        map<pair<double, double>, vector<map<string, double>>>
        frequencyEstimation(vector<pair<double, double>> params) {
            map<pair<double, double>, vector<map<string, double>>> res;
            std::random_device rd;

            map<pair<double, double>, vector<HyperLogLog>> param_to_hll_arr;
            for (const auto &param : params) {
                double eps = param.first;
                double delta = param.second;
                auto counters = static_cast<int>(pow((3 / eps), 2));
                auto bits = static_cast<int>(log2(counters));
                vector<HyperLogLog> hll_arr;
                for (int I = 0; I < ITERATIONS; I++) {
                    HyperLogLog hll(static_cast<uint8_t>(bits));
                    hll.set_seed(rd());
                    hll_arr.push_back(hll);
                }
                param_to_hll_arr.insert(pair<pair<double, double>, vector<HyperLogLog>>(param, hll_arr));
            }

            map<pair<double, double>, vector<Heap *>> param_to_heap_arr;
            for (const auto &param : params) {
                double eps = param.first;
                double delta = param.second;
                auto chi = static_cast<unsigned int>(ceil(3.0 / (eps / 2 * eps / 2) * log2(2.0 / delta / 2)));
                vector<Heap *> heap_arr;
                for (int I = 0; I < ITERATIONS; I++) {
                    auto *heap = new Heap(chi);
                    heap_arr.push_back(heap);
                }
                param_to_heap_arr.insert(pair<pair<double, double>, vector<Heap *>>(param, heap_arr));
            }

            int num_pkts = 0;
            std::clock_t start;
            start = std::clock();
            double duration = (std::clock() - start) / (double) CLOCKS_PER_SEC;
            cout << "pkts:" << num_pkts << " duration:" << duration << "[s]" << endl;
            auto pkt = pr.getNextIPPacket();
            while (pkt != nullptr) {
                ++num_pkts;
                if (num_pkts > stoi(getFrequencyLimit(dataset))) {
                    break;
                }
                const auto &pkt_string = pkt->getReprString();
                for (const auto &param : params) {
                    for (auto &hll : param_to_hll_arr.at(param)) {
                        hll.add(pkt_string.c_str(), static_cast<int>(pkt_string.size()));
                    }
                    for (auto &heap : param_to_heap_arr.at(param)) {
                        heap->Add(pkt->getReprString());
                    }
                }
                delete pkt;
                if (num_pkts % 1000000 == 0) {
                    duration = (std::clock() - start) / (double) CLOCKS_PER_SEC;
                    cout << "pkts:" << num_pkts << " duration:" << duration << "[s]" << endl;
                }
                pkt = pr.getNextIPPacket();
            }
            for (const auto &param : params) {
                double eps = param.first;
                double delta = param.second;
                double chi = ceil(3.0 / (eps / 2 * eps / 2) * log2(2.0 / delta / 2));
                cout << eps << " " << delta << " " << chi << endl;

                vector<double> Ps;
                vector<map<string, double>> samples;

                for (auto &hll : param_to_hll_arr.at(param)) {
                    double d = hll.estimate();
                    cout << "hll.estimate:" << d << endl;
                    Ps.push_back(chi / hll.estimate());
                }
                for (auto &heap : param_to_heap_arr.at(param)) {
                    const auto &s = heap->GetSample();
                    samples.push_back(s);
                }

                for (int k = 0; k < samples.size(); ++k) {
                    auto &sample = samples[k];
                    auto p = Ps[k];
                    cout << "p:" << p << endl;
                    for (const auto &s : sample) {
                        cout << "flow:" << s.first << endl;
                        cout << "sampled_frequency:" << s.second << endl;
                        double estimated_frequency = s.second / p;
                        sample[s.first] = estimated_frequency;
                        cout << "estimated_frequency:" << estimated_frequency << endl;
                    }
                    res[param] = samples;
                }
            }
            cout << "Finished preparing samples" << endl;

            for (const auto &param : params) {
                for (auto &heap : param_to_heap_arr.at(param)) {
                    delete heap;
                }
            }
            cout << "Finished deleting heaps" << endl;
            return res;
        }
    };
}

#endif //TDHH_ROUTER_HPP


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

#include <boost/math/concepts/real_concept.hpp>
#include <boost/test/included/test_exec_monitor.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <boost/math/special_functions/erf.hpp>
#include <boost/math/constants/constants.hpp>
#include <boost/type_traits/is_floating_point.hpp>
#include <boost/array.hpp>
#include "functor.hpp"

#include "test_erf_hooks.hpp"
#include "handle_test_result.hpp"

//
// DESCRIPTION:
// ~~~~~~~~~~~~
//
// This file tests the functions erf, erfc, and the inverses
// erf_inv and erfc_inv.  There are two sets of tests, spot
// tests which compare our results with selected values computed
// using the online special function calculator at
// functions.wolfram.com, while the bulk of the accuracy tests
// use values generated with NTL::RR at 1000-bit precision
// and our generic versions of these functions.
//
// Note that when this file is first run on a new platform many of
// these tests will fail: the default accuracy is 1 epsilon which
// is too tight for most platforms.  In this situation you will
// need to cast a human eye over the error rates reported and make
// a judgement as to whether they are acceptable.  Either way please
// report the results to the Boost mailing list.  Acceptable rates of
// error are marked up below as a series of regular expressions that
// identify the compiler/stdlib/platform/data-type/test-data/test-function
// along with the maximum expected peek and RMS mean errors for that
// test.
//

void expected_results()
{
   //
   // Define the max and mean errors expected for
   // various compilers and platforms.
   //
   const char* largest_type;
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
   if(boost::math::policies::digits<double, boost::math::policies::policy<> >() == boost::math::policies::digits<long double, boost::math::policies::policy<> >())
   {
      largest_type = "(long\\s+)?double|real_concept";
   }
   else
   {
      largest_type = "long double|real_concept";
   }
#else
   largest_type = "(long\\s+)?double";
#endif
   //
   // On MacOS X erfc has much higher error levels than
   // expected: given that the implementation is basically
   // just a rational function evaluation combined with
   // exponentiation, we conclude that exp and pow are less
   // accurate on this platform, especially when the result
   // is outside the range of a double.
   //
   add_expected_result(
      ".*",                          // compiler
      ".*",                          // stdlib
      "Mac OS",                      // platform
      largest_type,                  // test type(s)
      "Erf Function:.*Large.*",      // test data group
      "boost::math::erfc", 4300, 1300);  // test function
   add_expected_result(
      ".*",                          // compiler
      ".*",                          // stdlib
      "Mac OS",                      // platform
      largest_type,                  // test type(s)
      "Erf Function:.*",             // test data group
      "boost::math::erfc", 40, 10);  // test function

   add_expected_result(
      ".*",                          // compiler
      ".*",                          // stdlib
      ".*",                          // platform
      "real_concept",                // test type(s)
      "Erf Function:.*",             // test data group
      "boost::math::erfc?", 20, 6);   // test function
   add_expected_result(
      ".*",                           // compiler
      ".*",                           // stdlib
      ".*",                           // platform
      "real_concept",                 // test type(s)
      "Inverse Erfc.*",               // test data group
      "boost::math::erfc_inv", 80, 10);  // test function

   //
   // Catch all cases come last:
   //
   add_expected_result(
      ".*",                          // compiler
      ".*",                          // stdlib
      ".*",                          // platform
      ".*",                          // test type(s)
      "Erf Function:.*",             // test data group
      "boost::math::erfc?", 2, 2);   // test function
   add_expected_result(
      ".*aCC.*",                     // compiler
      ".*",                          // stdlib
      ".*",                          // platform
      ".*",                          // test type(s)
      "Inverse Erfc.*",               // test data group
      "boost::math::erfc_inv", 80, 10);  // test function
   add_expected_result(
      ".*",                          // compiler
      ".*",                          // stdlib
      ".*",                          // platform
      ".*",                          // test type(s)
      "Inverse Erf.*",               // test data group
      "boost::math::erfc?_inv", 18, 4);  // test function

   //
   // Finish off by printing out the compiler/stdlib/platform names,
   // we do this to make it easier to mark up expected error rates.
   //
   std::cout << "Tests run with " << BOOST_COMPILER << ", "
      << BOOST_STDLIB << ", " << BOOST_PLATFORM << std::endl;
}

template <class T>
void do_test_erf(const T& data, const char* type_name, const char* test_name)
{
   typedef typename T::value_type row_type;
   typedef typename row_type::value_type value_type;

   typedef value_type (*pg)(value_type);
#if defined(BOOST_MATH_NO_DEDUCED_FUNCTION_POINTERS)
   pg funcp = boost::math::erf<value_type>;
#else
   pg funcp = boost::math::erf;
#endif

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

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

   //
   // test erf against data:
   //
   result = boost::math::tools::test(
      data,
      bind_func(funcp, 0),
      extract_result(1));
   handle_test_result(result, data[result.worst()], result.worst(), type_name, "boost::math::erf", test_name);
#ifdef TEST_OTHER
   if(::boost::is_floating_point<value_type>::value){
      funcp = other::erf;
      result = boost::math::tools::test(
         data,
         bind_func(funcp, 0),
         extract_result(1));
      print_test_result(result, data[result.worst()], result.worst(), type_name, "other::erf");
   }
#endif
   //
   // test erfc against data:
   //
#if defined(BOOST_MATH_NO_DEDUCED_FUNCTION_POINTERS)
   funcp = boost::math::erfc<value_type>;
#else
   funcp = boost::math::erfc;
#endif
   result = boost::math::tools::test(
      data,
      bind_func(funcp, 0),
      extract_result(2));
   handle_test_result(result, data[result.worst()], result.worst(), type_name, "boost::math::erfc", test_name);
#ifdef TEST_OTHER
   if(::boost::is_floating_point<value_type>::value){
      funcp = other::erfc;
      result = boost::math::tools::test(
         data,
         bind(funcp, 0),
         extract_result(2));
      print_test_result(result, data[result.worst()], result.worst(), type_name, "other::erfc");
   }
#endif
   std::cout << std::endl;
}

template <class T>
void do_test_erf_inv(const T& data, const char* type_name, const char* test_name)
{
   typedef typename T::value_type row_type;
   typedef typename row_type::value_type value_type;

   typedef value_type (*pg)(value_type);

   boost::math::tools::test_result<value_type> result;
   std::cout << "Testing " << test_name << " with type " << type_name
      << "\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";
   //
   // test erf_inv against data:
   //
#if defined(BOOST_MATH_NO_DEDUCED_FUNCTION_POINTERS)
   pg funcp = boost::math::erf_inv<value_type>;
#else
   pg funcp = boost::math::erf_inv;
#endif
   result = boost::math::tools::test(
      data,
      bind_func(funcp, 0),
      extract_result(1));
   handle_test_result(result, data[result.worst()], result.worst(), type_name, "boost::math::erf_inv", test_name);
   std::cout << std::endl;
}

template <class T>
void do_test_erfc_inv(const T& data, const char* type_name, const char* test_name)
{
   typedef typename T::value_type row_type;
   typedef typename row_type::value_type value_type;

   typedef value_type (*pg)(value_type);
#if defined(BOOST_MATH_NO_DEDUCED_FUNCTION_POINTERS)
   pg funcp = boost::math::erf<value_type>;
#else
   pg funcp = boost::math::erf;
#endif

   boost::math::tools::test_result<value_type> result;
   std::cout << "Testing " << test_name << " with type " << type_name
      << "\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";
   //
   // test erfc_inv against data:
   //
#if defined(BOOST_MATH_NO_DEDUCED_FUNCTION_POINTERS)
   funcp = boost::math::erfc_inv<value_type>;
#else
   funcp = boost::math::erfc_inv;
#endif
   result = boost::math::tools::test(
      data,
      bind_func(funcp, 0),
      extract_result(1));
   handle_test_result(result, data[result.worst()], result.worst(), type_name, "boost::math::erfc_inv", test_name);
   std::cout << std::endl;
}

template <class T>
void test_erf(T, const char* name)
{
   //
   // The actual test data is rather verbose, so it's in a separate file
   //
   // The contents are as follows, each row of data contains
   // three items, input value a, input value b and erf(a, b):
   //
#  include "erf_small_data.ipp"

   do_test_erf(erf_small_data, name, "Erf Function: Small Values");

#  include "erf_data.ipp"

   do_test_erf(erf_data, name, "Erf Function: Medium Values");

#  include "erf_large_data.ipp"

   do_test_erf(erf_large_data, name, "Erf Function: Large Values");

#  include "erf_inv_data.ipp"

   do_test_erf_inv(erf_inv_data, name, "Inverse Erf Function");

#  include "erfc_inv_data.ipp"

   do_test_erfc_inv(erfc_inv_data, name, "Inverse Erfc Function");

#  include "erfc_inv_big_data.ipp"

   if(std::numeric_limits<T>::min_exponent <= -4500)
   {
      do_test_erfc_inv(erfc_inv_big_data, name, "Inverse Erfc Function: extreme values");
   }
}

template <class T>
void test_spots(T, const char* t)
{
   std::cout << "Testing basic sanity checks for type " << t << std::endl;
   //
   // basic sanity checks, tolerance is 10 epsilon expressed as a percentage:
   //
   T tolerance = boost::math::tools::epsilon<T>() * 1000;
   BOOST_CHECK_CLOSE(::boost::math::erfc(static_cast<T>(0.125)), static_cast<T>(0.85968379519866618260697055347837660181302041685015L), tolerance);
   BOOST_CHECK_CLOSE(::boost::math::erfc(static_cast<T>(0.5)), static_cast<T>(0.47950012218695346231725334610803547126354842424204L), tolerance);
   BOOST_CHECK_CLOSE(::boost::math::erfc(static_cast<T>(1)), static_cast<T>(0.15729920705028513065877936491739074070393300203370L), tolerance);
   BOOST_CHECK_CLOSE(::boost::math::erfc(static_cast<T>(5)), static_cast<T>(1.5374597944280348501883434853833788901180503147234e-12L), tolerance);
   BOOST_CHECK_CLOSE(::boost::math::erfc(static_cast<T>(-0.125)), static_cast<T>(1.1403162048013338173930294465216233981869795831498L), tolerance);
   BOOST_CHECK_CLOSE(::boost::math::erfc(static_cast<T>(-0.5)), static_cast<T>(1.5204998778130465376827466538919645287364515757580L), tolerance);
   BOOST_CHECK_CLOSE(::boost::math::erfc(static_cast<T>(0)), static_cast<T>(1), tolerance);

   BOOST_CHECK_CLOSE(::boost::math::erf(static_cast<T>(0.125)), static_cast<T>(0.14031620480133381739302944652162339818697958314985L), tolerance);
   BOOST_CHECK_CLOSE(::boost::math::erf(static_cast<T>(0.5)), static_cast<T>(0.52049987781304653768274665389196452873645157575796L), tolerance);
   BOOST_CHECK_CLOSE(::boost::math::erf(static_cast<T>(1)), static_cast<T>(0.84270079294971486934122063508260925929606699796630L), tolerance);
   BOOST_CHECK_CLOSE(::boost::math::erf(static_cast<T>(5)), static_cast<T>(0.9999999999984625402055719651498116565146166211099L), tolerance);
   BOOST_CHECK_CLOSE(::boost::math::erf(static_cast<T>(-0.125)), static_cast<T>(-0.14031620480133381739302944652162339818697958314985L), tolerance);
   BOOST_CHECK_CLOSE(::boost::math::erf(static_cast<T>(-0.5)), static_cast<T>(-0.52049987781304653768274665389196452873645157575796L), tolerance);
   BOOST_CHECK_CLOSE(::boost::math::erf(static_cast<T>(0)), static_cast<T>(0), tolerance);

   tolerance = boost::math::tools::epsilon<T>() * 100 * 200; // 200 eps %.
#if defined(__CYGWIN__)
   // some platforms long double is only reliably accurate to double precision:
   if(sizeof(T) == sizeof(long double))
      tolerance = boost::math::tools::epsilon<double>() * 100 * 200; // 200 eps %.
#endif

   for(T i = -0.95f; i < 1; i += 0.125f)
   {
      T inv = boost::math::erf_inv(i);
      T b = boost::math::erf(inv);
      BOOST_CHECK_CLOSE(b, i, tolerance);
   }
   for(T j = 0.125f; j < 2; j += 0.125f)
   {
      T inv = boost::math::erfc_inv(j);
      T b = boost::math::erfc(inv);
      BOOST_CHECK_CLOSE(b, j, tolerance);
   }
}

int test_main(int, char* [])
{
   BOOST_MATH_CONTROL_FP;
   test_spots(0.0F, "float");
   test_spots(0.0, "double");
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
   test_spots(0.0L, "long double");
#if !BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582))
   test_spots(boost::math::concepts::real_concept(0.1), "real_concept");
#endif
#endif

   expected_results();

   test_erf(0.1F, "float");
   test_erf(0.1, "double");
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
   test_erf(0.1L, "long double");
#ifndef BOOST_MATH_NO_REAL_CONCEPT_TESTS
#if !BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582))
   test_erf(boost::math::concepts::real_concept(0.1), "real_concept");
#endif
#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::cout;
#endif
   return 0;
}

/*

Output:

test_erf.cpp
Compiling manifest to resources...
Linking...
Embedding manifest...
Autorun "i:\boost-06-05-03-1300\libs\math\test\Math_test\debug\test_erf.exe"
Running 1 test case...
Testing basic sanity checks for type float
Testing basic sanity checks for type double
Testing basic sanity checks for type long double
Testing basic sanity checks for type real_concept
Tests run with Microsoft Visual C++ version 8.0, Dinkumware standard library version 405, Win32
Testing Erf Function: Small Values with type float
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boost::math::erf<float> Max = 0 RMS Mean=0
boost::math::erfc<float> Max = 0 RMS Mean=0
Testing Erf Function: Medium Values with type float
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boost::math::erf<float> Max = 0 RMS Mean=0
boost::math::erfc<float> Max = 0 RMS Mean=0
Testing Erf Function: Large Values with type float
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boost::math::erf<float> Max = 0 RMS Mean=0
boost::math::erfc<float> Max = 0 RMS Mean=0
Testing Inverse Erf Function with type float
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boost::math::erf_inv<float> Max = 0 RMS Mean=0
Testing Inverse Erfc Function with type float
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boost::math::erfc_inv<float> Max = 0 RMS Mean=0
Testing Erf Function: Small Values with type double
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boost::math::erf<double> Max = 0 RMS Mean=0
boost::math::erfc<double> Max = 0.7857 RMS Mean=0.06415
    worst case at row: 149
    { 0.3343, 0.3636, 0.6364 }
Testing Erf Function: Medium Values with type double
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boost::math::erf<double> Max = 0.9219 RMS Mean=0.1016
    worst case at row: 273
    { 0.5252, 0.5424, 0.4576 }
boost::math::erfc<double> Max = 1.08 RMS Mean=0.3224
    worst case at row: 287
    { 0.8461, 0.7685, 0.2315 }
Testing Erf Function: Large Values with type double
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boost::math::erf<double> Max = 0 RMS Mean=0
boost::math::erfc<double> Max = 1.048 RMS Mean=0.2032
    worst case at row: 50
    { 20.96, 1, 4.182e-193 }
Testing Inverse Erf Function with type double
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boost::math::erf_inv<double> Max = 1.124 RMS Mean=0.5082
    worst case at row: 98
    { 0.9881, 1.779 }
Testing Inverse Erfc Function with type double
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boost::math::erfc_inv<double> Max = 1.124 RMS Mean=0.5006
    worst case at row: 98
    { 1.988, -1.779 }
Testing Erf Function: Small Values with type long double
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boost::math::erf<long double> Max = 0 RMS Mean=0
boost::math::erfc<long double> Max = 0.7857 RMS Mean=0.06415
    worst case at row: 149
    { 0.3343, 0.3636, 0.6364 }
Testing Erf Function: Medium Values with type long double
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boost::math::erf<long double> Max = 0.9219 RMS Mean=0.1016
    worst case at row: 273
    { 0.5252, 0.5424, 0.4576 }
boost::math::erfc<long double> Max = 1.08 RMS Mean=0.3224
    worst case at row: 287
    { 0.8461, 0.7685, 0.2315 }
Testing Erf Function: Large Values with type long double
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boost::math::erf<long double> Max = 0 RMS Mean=0
boost::math::erfc<long double> Max = 1.048 RMS Mean=0.2032
    worst case at row: 50
    { 20.96, 1, 4.182e-193 }
Testing Inverse Erf Function with type long double
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boost::math::erf_inv<long double> Max = 1.124 RMS Mean=0.5082
    worst case at row: 98
    { 0.9881, 1.779 }
Testing Inverse Erfc Function with type long double
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boost::math::erfc_inv<long double> Max = 1.124 RMS Mean=0.5006
    worst case at row: 98
    { 1.988, -1.779 }
Testing Erf Function: Small Values with type real_concept
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boost::math::erf<real_concept> Max = 1.271 RMS Mean=0.5381
    worst case at row: 144
    { 0.0109, 0.0123, 0.9877 }
boost::math::erfc<real_concept> Max = 0.7857 RMS Mean=0.07777
    worst case at row: 149
    { 0.3343, 0.3636, 0.6364 }
Testing Erf Function: Medium Values with type real_concept
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boost::math::erf<real_concept> Max = 22.5 RMS Mean=4.224
    worst case at row: 233
    { -0.7852, -0.7332, 1.733 }
Peak error greater than expected value of 20
i:/boost-sandbox/math_toolkit/libs/math/test/handle_test_result.hpp(146): error in "test_main_caller( argc, argv )": check bounds.first >= max_error_found failed
boost::math::erfc<real_concept> Max = 97.77 RMS Mean=8.373
    worst case at row: 289
    { 0.9849, 0.8363, 0.1637 }
Peak error greater than expected value of 20
i:/boost-sandbox/math_toolkit/libs/math/test/handle_test_result.hpp(146): error in "test_main_caller( argc, argv )": check bounds.first >= max_error_found failed
Mean error greater than expected value of 6
i:/boost-sandbox/math_toolkit/libs/math/test/handle_test_result.hpp(151): error in "test_main_caller( argc, argv )": check bounds.second >= mean_error_found failed
Testing Erf Function: Large Values with type real_concept
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boost::math::erf<real_concept> Max = 0 RMS Mean=0
boost::math::erfc<real_concept> Max = 1.395 RMS Mean=0.2908
    worst case at row: 11
    { 10.99, 1, 1.87e-054 }
Testing Inverse Erf Function with type real_concept
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boost::math::erf_inv<real_concept> Max = 1.124 RMS Mean=0.5082
    worst case at row: 98
    { 0.9881, 1.779 }
Testing Inverse Erfc Function with type real_concept
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boost::math::erfc_inv<real_concept> Max = 1.124 RMS Mean=0.5006
    worst case at row: 98
    { 1.988, -1.779 }
Test suite "Test Program" failed with:
  181 assertions out of 184 passed
  3 assertions out of 184 failed
  1 test case out of 1 failed
  Test case "test_main_caller( argc, argv )" failed with:
    181 assertions out of 184 passed
    3 assertions out of 184 failed
Build Time 0:15

*/


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

  @copyright 2016 NumScale SAS

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

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{

 /*!

    @ingroup group-trigonometric
    Function object implementing asinpi capabilities

    inverse sine in \f$\pi\f$ multiples.
    @par Semantic:

    For every parameter of floating type

    @code
    auto r = asinpi(x);
    @endcode

    Returns the arc @c r in the interval
    \f$[-0.5, 0.5[\f$ such that <tt>cos(r) == x</tt>.
    If @c x is outside \f$[-1, 1[\f$ the result is Nan.

    @see asin, asind, sinpi
  **/
  Value asinpi(Value const & v0);
} }
#endif

#include <boost/simd/function/scalar/asinpi.hpp>
#include <boost/simd/function/simd/asinpi.hpp>

#endif


/** @file components.cc
 * @author David F. Gleich
 * @date 2006-04-19
 * @copyright Stanford University, 2006-2008
 * Implement wrappers for connected component functions
 */

/** History
 *  2006-04-19: Initial version
 *  2007-07-09: Updated to use simple_csr_matrix graph type
 */

#include "include/matlab_bgl.h"

#include <yasmic/simple_csr_matrix_as_graph.hpp>
#include <yasmic/iterator_utility.hpp>
#include <boost/graph/biconnected_components.hpp>
#include <boost/graph/strong_components.hpp>

int strong_components(
    mbglIndex nverts, mbglIndex *ja, mbglIndex *ia, /* connectivity params */
    mbglIndex* ci)
{
  using namespace yasmic;
  using namespace boost;

  typedef simple_csr_matrix<mbglIndex, double> crs_graph;
  crs_graph g(nverts, nverts, ia[nverts], ia, ja, NULL);

  strong_components(g, make_iterator_property_map(ci, get(vertex_index, g)));

  return 0;
}

/**
 * Wrap a boost graph library call to biconnected_components.
 *
 * the ja and ia arrays specify the connectivity of the underlying graph,
 * ia is a length (nverts+1) array with the indices in ja that start the
 * nonzeros in each row.  ja is a length (ia(nverts)) array with the
 * columns of the connectivity.
 *
 * if a or ci is NULL, then that parameter is not computed.
 *
 * @param nverts the number of vertices in the graph
 * @param ja the connectivity for each vertex
 * @param ia the row connectivity points into ja
 * @param a an array which will store the articulaion points of the graph
 *     the array length should be n
 * @param ci the component index array which is length (nnz)
 * @return an error code if possible
 */

int biconnected_components(
    mbglIndex nverts, mbglIndex *ja, mbglIndex *ia, /* connectivity params */
    mbglIndex* a, mbglIndex* ci)
{
  using namespace yasmic;
  using namespace boost;

  typedef simple_csr_matrix<mbglIndex, double> crs_graph;
  crs_graph g(nverts, nverts, ia[nverts], ia, ja, NULL);

  if (a) {
    if (ci) {
      std::size_t num_bicomps;
      mbglIndex *oi;
      boost::tie(num_bicomps, oi) = biconnected_components(g,
          make_iterator_property_map(ci, get(edge_index, g)), a);
    } else {
      articulation_points(g, a);
    }
  } else {
    biconnected_components(g,
        make_iterator_property_map(ci, get(edge_index, g)));
  }

  return 0;
}


#include "gtest/gtest.h"
#include <Eigen/Core>
#include "MeshFactory.h"

namespace {

  using namespace Geotree;

  class MeshFactoryTest : public ::testing::Test {
  protected:
    MeshFactoryTest()
    {
    }
    ~MeshFactoryTest()
    {
    }
    MeshFactory factory;
  };

  void verifyCube(Mesh cube, double x, double y, double z)
  {
    EXPECT_EQ(12, cube.size());
    EXPECT_EQ(12, cube.rowcount());
    EXPECT_EQ(8, cube.V.rows());

    EXPECT_EQ(4*(x+y+z), cube.V.sum());

    EXPECT_TRUE( (cube.V.array() == x ||
		  cube.V.array() == y ||
		  cube.V.array() == z ||
		  cube.V.array() == 0.0).all());

    EXPECT_GE((cube.V.array() == x).count(), 4);
    EXPECT_GE((cube.V.array() == y).count(), 4);
    EXPECT_GE((cube.V.array() == z).count(), 4);
    EXPECT_EQ(12, (cube.V.array() == 0.0).count());

    EXPECT_EQ(1, (cube.V.rowwise().sum().array() == 0.0).count());
    EXPECT_GE((cube.V.rowwise().sum().array() == x).count(), 1);
    EXPECT_GE((cube.V.rowwise().sum().array() == y).count(), 1);
    EXPECT_GE((cube.V.rowwise().sum().array() == z).count(), 1);
    EXPECT_GE((cube.V.rowwise().sum().array() == x+y).count(), 1);
    EXPECT_GE((cube.V.rowwise().sum().array() == x+z).count(), 1);
    EXPECT_GE((cube.V.rowwise().sum().array() == y+z).count(), 1);
    EXPECT_GE((cube.V.rowwise().sum().array() == x+y+z).count(), 1);

    EXPECT_EQ(x*4, cube.V.col(0).sum());
    EXPECT_EQ(y*4, cube.V.col(1).sum());
    EXPECT_EQ(z*4, cube.V.col(2).sum());
  }

  void nullCube(Mesh cube)
  {
    EXPECT_EQ(0, cube.size());
    EXPECT_EQ(0, cube.V.rows());
    EXPECT_EQ(0, cube.rowcount());
  }

  TEST_F(MeshFactoryTest, cube)
  {
    Mesh cube = factory.makeCube(10,10,10);

    EXPECT_EQ(12, cube.size());
    EXPECT_EQ(8, cube.V.rows());

    EXPECT_EQ(120.0, cube.V.sum());

    EXPECT_EQ(12,(cube.V.array() == 10.0).count() );

    EXPECT_EQ(1, (cube.V.rowwise().sum().array() == 30.0).count());
    EXPECT_EQ(3, (cube.V.rowwise().sum().array() == 20.0).count());
    EXPECT_EQ(3, (cube.V.rowwise().sum().array() == 10.0).count());

    EXPECT_EQ(40.0, cube.V.col(0).sum());
    EXPECT_EQ(40.0, cube.V.col(1).sum());
    EXPECT_EQ(40.0, cube.V.col(2).sum());
  }

  TEST_F(MeshFactoryTest, cubesVerify)
  {
    verifyCube(factory.makeCube(10, 10, 10), 10.0, 10.0, 10.0);
    verifyCube(factory.makeCube(1.0, 2.0, 3.0), 1.0, 2.0, 3.0);
    verifyCube(factory.makeCube(1.0, 1.0, 2.0), 1.0, 1.0, 2.0);
    verifyCube(factory.makeCube(1.0, 2.0, 1.0), 1.0, 2.0, 1.0);
    verifyCube(factory.makeCube(0.0001, 0.567, 100.3), 0.0001, 0.567, 100.3);
  }

  TEST_F(MeshFactoryTest, cubeInvalid)
  {
    nullCube(factory.makeCube(0,0,0));
    nullCube(factory.makeCube(0.0, 10, 10));
    nullCube(factory.makeCube(10, 0.0, 10));
    nullCube(factory.makeCube(10, 10, 0.0));
    nullCube(factory.makeCube(-1.0, 10, 10));
    nullCube(factory.makeCube(10, -1.0, 10));
    nullCube(factory.makeCube(10, 10, -1.0));
  }

    TEST_F(MeshFactoryTest, tetra)
    {
      Mesh tetra = factory.makeTetra(10);
    }

  TEST_F(MeshFactoryTest, DISABLED_sphere)
  {
    Mesh sphere = factory.makeSphere(10);

    EXPECT_EQ(20, sphere.size());
  }

  TEST_F(MeshFactoryTest, DISABLED_cylinder)
  {
    Mesh cylinder = factory.makeCylinder(10, 10);

    EXPECT_EQ(20, cylinder.size());
  }

    //  TEST_F(MeshTest, translate)
//  {
//    Mesh cube = mf.makeCube(10,10,10);
//    Mesh cubeTranslate;
//
//    cubeTranslate = cube;
//    cubeTranslate.translate(Vertex(10,0,0));
//    verifyTranslate(cube, cubeTranslate, 10,0,0);
//
//    cubeTranslate = cube;
//    cubeTranslate.translate(Vertex(0,0,0));
//    verifyTranslate(cube, cubeTranslate, 0,0,0);
//
//    cubeTranslate = cube;
//    cubeTranslate.translate(Vertex(-10.1, -55.2, -3.2));
//    verifyTranslate(cube, cubeTranslate, -10.1, -55.2, -3.2);
//  }
}


/* ---------------------------------------------------------------------
 *
 * Copyright (C) 2006 - 2020 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: Ivan Christov, Wolfgang Bangerth, Texas A&M University, 2006
 */

// @sect3{Include files and global variables}

// 关于包含文件的解释，读者应该参考示例程序  step-1  到  step-4  。它们的标准顺序是  base -- lac -- grid  --  dofs -- fe -- numerics  （因为每一类大致都是建立在前面的基础上），然后是一些用于文件输入/输出和字符串流的C++头文件。

#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/dofs/dof_handler.h>
#include <deal.II/dofs/dof_tools.h>

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

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

#include <fstream>
#include <iostream>

// 最后一步和以前所有的程序一样。

namespace Step25
{
  using namespace dealii;
// @sect3{The SineGordonProblem class template}

// 解决问题的整个算法被封装在这个类中。和以前的例子程序一样，这个类在声明时有一个模板参数，就是空间维度，这样我们就可以在一个、两个或三个空间维度上解决正弦-戈登方程。关于这个问题的独立于维度的类封装的更多信息，读者应该参考  step-3  和  step-4  。

// 与 step-23 和 step-24 相比，在程序的总体结构中没有任何值得注意的地方（当然，在各种函数的内部运作中也有！）。最明显的区别是出现了两个新的函数 compute_nl_term 和 compute_nl_matrix ，计算系统矩阵的非线性贡献和第一个方程的右手边，正如在介绍中讨论的那样。此外，我们还必须有一个向量 solution_update ，它包含在每个牛顿步骤中对解向量的非线性更新。

// 正如介绍中也提到的，我们在这个程序中不存储速度变量，而是质量矩阵乘以速度。这是在 M_x_velocity 变量中完成的（"x "是代表 "次数"）。

// 最后， output_timestep_skip 变量存储了在生成图形输出前每次所需的时间步数。这一点在使用精细网格（因此时间步数较小）时非常重要，在这种情况下，我们会运行大量的时间步数，并创建大量的输出文件，这些文件中的解决方案在后续文件中看起来几乎是一样的。这只会堵塞我们的可视化程序，我们应该避免创建比我们真正感兴趣的更多的输出。因此，如果这个变量被设置为大于1的值 $n$ ，那么只有在每一个 $n$ 的时间步长时才会产生输出。

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

  private:
    void         make_grid_and_dofs();
    void         assemble_system();
    void         compute_nl_term(const Vector<double> &old_data,
                                 const Vector<double> &new_data,
                                 Vector<double> &      nl_term) const;
    void         compute_nl_matrix(const Vector<double> &old_data,
                                   const Vector<double> &new_data,
                                   SparseMatrix<double> &nl_matrix) const;
    unsigned int solve();
    void         output_results(const unsigned int timestep_number) const;

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

    SparsityPattern      sparsity_pattern;
    SparseMatrix<double> system_matrix;
    SparseMatrix<double> mass_matrix;
    SparseMatrix<double> laplace_matrix;

    const unsigned int n_global_refinements;

    double       time;
    const double final_time, time_step;
    const double theta;

    Vector<double> solution, solution_update, old_solution;
    Vector<double> M_x_velocity;
    Vector<double> system_rhs;

    const unsigned int output_timestep_skip;
  };
// @sect3{Initial conditions}

// 在下面两类中，我们首先实现了本程序介绍中提到的一维、二维和三维的精确解。如果想通过比较数值解和分析解来测试程序的准确性，那么这个时空解可能会有独立的意义（但是请注意，程序使用的是有限域，而这些是无界域的分析解）。例如，这可以用 VectorTools::integrate_difference 函数来完成。再次注意（正如在 step-23 中已经讨论过的），我们如何将时空函数描述为依赖于时间变量的空间函数，该变量可以使用FunctionTime基类的 FunctionTime::set_time() 和 FunctionTime::get_time() 成员函数进行设置和查询。

  template <int dim>
  class ExactSolution : public Function<dim>
  {
  public:
    ExactSolution(const unsigned int n_components = 1, const double time = 0.)
      : Function<dim>(n_components, time)
    {}

    virtual double value(const Point<dim> &p,
                         const unsigned int /*component*/ = 0) const override
    {
      const double t = this->get_time();

      switch (dim)
        {
          case 1:
            {
              const double m  = 0.5;
              const double c1 = 0.;
              const double c2 = 0.;
              return -4. * std::atan(m / std::sqrt(1. - m * m) *
                                     std::sin(std::sqrt(1. - m * m) * t + c2) /
                                     std::cosh(m * p[0] + c1));
            }

          case 2:
            {
              const double theta  = numbers::PI / 4.;
              const double lambda = 1.;
              const double a0     = 1.;
              const double s      = 1.;
              const double arg    = p[0] * std::cos(theta) +
                                 std::sin(theta) * (p[1] * std::cosh(lambda) +
                                                    t * std::sinh(lambda));
              return 4. * std::atan(a0 * std::exp(s * arg));
            }

          case 3:
            {
              const double theta = numbers::PI / 4;
              const double phi   = numbers::PI / 4;
              const double tau   = 1.;
              const double c0    = 1.;
              const double s     = 1.;
              const double arg   = p[0] * std::cos(theta) +
                                 p[1] * std::sin(theta) * std::cos(phi) +
                                 std::sin(theta) * std::sin(phi) *
                                   (p[2] * std::cosh(tau) + t * std::sinh(tau));
              return 4. * std::atan(c0 * std::exp(s * arg));
            }

          default:
            Assert(false, ExcNotImplemented());
            return -1e8;
        }
    }
  };

// 在本节的第二部分，我们提供初始条件。我们很懒惰（也很谨慎），不想第二次实现与上面相同的函数。相反，如果我们被查询到初始条件，我们会创建一个对象 ExactSolution ，将其设置为正确的时间，并让它计算当时的精确解的任何值。

  template <int dim>
  class InitialValues : public Function<dim>
  {
  public:
    InitialValues(const unsigned int n_components = 1, const double time = 0.)
      : Function<dim>(n_components, time)
    {}

    virtual double value(const Point<dim> & p,
                         const unsigned int component = 0) const override
    {
      return ExactSolution<dim>(1, this->get_time()).value(p, component);
    }
  };
// @sect3{Implementation of the SineGordonProblem class}

// 让我们继续讨论主类的实现，因为它实现了介绍中概述的算法。

//  @sect4{SineGordonProblem::SineGordonProblem}

// 这是 SineGordonProblem 类的构造函数。它指定了所需的有限元的多项式程度，关联了一个 DoFHandler to the triangulation 对象（就像在示例程序 step-3 和 step-4 中一样），初始化了当前或初始时间、最终时间、时间步长，以及时间步长方案的 $\theta$ 值。由于我们在这里计算的解是时间周期性的，所以开始时间的实际值并不重要，我们选择它是为了让我们在一个有趣的时间开始。

// 请注意，如果我们选择显式欧拉时间步进方案（ $\theta = 0$ ），那么我们必须选择一个时间步进 $k \le h$ ，否则该方案不稳定，解中可能出现振荡。Crank-Nicolson方案（ $\theta = \frac{1}{2}$ ）和隐式Euler方案（ $\theta=1$ ）不存在这个缺陷，因为它们是无条件稳定的。然而，即使如此，时间步长也应选择在 $h$ 的数量级上，以获得一个好的解决方案。由于我们知道我们的网格是由矩形的均匀细分而来，我们可以很容易地计算出这个时间步长；如果我们有一个不同的域， step-24 中的技术使用 GridTools::minimal_cell_diameter 也是可以的。

  template <int dim>
  SineGordonProblem<dim>::SineGordonProblem()
    : fe(1)
    , dof_handler(triangulation)
    , n_global_refinements(6)
    , time(-5.4414)
    , final_time(2.7207)
    , time_step(10 * 1. / std::pow(2., 1. * n_global_refinements))
    , theta(0.5)
    , output_timestep_skip(1)
  {}
// @sect4{SineGordonProblem::make_grid_and_dofs}

// 这个函数创建了一个 dim 维度的矩形网格，并对其进行了多次细化。同时，一旦自由度被集合起来， SineGordonProblem 类的所有矩阵和向量成员都被初始化为相应的大小。像 step-24 一样，我们使用 MatrixCreator 函数来生成质量矩阵 $M$ 和拉普拉斯矩阵 $A$ ，并在程序的剩余时间里将它们存储在适当的变量中。

  template <int dim>
  void SineGordonProblem<dim>::make_grid_and_dofs()
  {
    GridGenerator::hyper_cube(triangulation, -10, 10);
    triangulation.refine_global(n_global_refinements);

    std::cout << "   Number of active cells: " << triangulation.n_active_cells()
              << std::endl
              << "   Total number of cells: " << triangulation.n_cells()
              << std::endl;

    dof_handler.distribute_dofs(fe);

    std::cout << "   Number of degrees of freedom: " << dof_handler.n_dofs()
              << std::endl;

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

    system_matrix.reinit(sparsity_pattern);
    mass_matrix.reinit(sparsity_pattern);
    laplace_matrix.reinit(sparsity_pattern);

    MatrixCreator::create_mass_matrix(dof_handler,
                                      QGauss<dim>(fe.degree + 1),
                                      mass_matrix);
    MatrixCreator::create_laplace_matrix(dof_handler,
                                         QGauss<dim>(fe.degree + 1),
                                         laplace_matrix);

    solution.reinit(dof_handler.n_dofs());
    solution_update.reinit(dof_handler.n_dofs());
    old_solution.reinit(dof_handler.n_dofs());
    M_x_velocity.reinit(dof_handler.n_dofs());
    system_rhs.reinit(dof_handler.n_dofs());
  }
// @sect4{SineGordonProblem::assemble_system}

// 这个函数为牛顿方法的每次迭代组装系统矩阵和右手向量。关于系统矩阵和右手边的明确公式，读者应该参考导论。

// 请注意，在每个时间步长中，我们必须把对矩阵和右手边的各种贡献加起来。与 step-23 和 step-24 相比，这需要集合更多的项，因为它们取决于前一个时间步骤或前一个非线性步骤的解。我们使用函数 compute_nl_matrix 和 compute_nl_term 来做到这一点，而本函数提供了顶层逻辑。

  template <int dim>
  void SineGordonProblem<dim>::assemble_system()
  {

// 首先我们组装雅各布矩阵 $F'_h(U^{n,l})$  ，其中 $U^{n,l}$ 为方便起见被储存在向量 solution 中。

    system_matrix.copy_from(mass_matrix);
    system_matrix.add(std::pow(time_step * theta, 2), laplace_matrix);

    SparseMatrix<double> tmp_matrix(sparsity_pattern);
    compute_nl_matrix(old_solution, solution, tmp_matrix);
    system_matrix.add(std::pow(time_step * theta, 2), tmp_matrix);

// 接下来我们计算右手边的向量。这只是介绍中对 $-F_h(U^{n,l})$ 的描述所暗示的矩阵-向量的组合。

    system_rhs = 0.;

    Vector<double> tmp_vector(solution.size());

    mass_matrix.vmult(system_rhs, solution);
    laplace_matrix.vmult(tmp_vector, solution);
    system_rhs.add(std::pow(time_step * theta, 2), tmp_vector);

    mass_matrix.vmult(tmp_vector, old_solution);
    system_rhs.add(-1.0, tmp_vector);
    laplace_matrix.vmult(tmp_vector, old_solution);
    system_rhs.add(std::pow(time_step, 2) * theta * (1 - theta), tmp_vector);

    system_rhs.add(-time_step, M_x_velocity);

    compute_nl_term(old_solution, solution, tmp_vector);
    system_rhs.add(std::pow(time_step, 2) * theta, tmp_vector);

    system_rhs *= -1.;
  }
// @sect4{SineGordonProblem::compute_nl_term}

// 这个函数计算向量 $S(\cdot,\cdot)$ ，它出现在分裂公式的两个方程的非线性项中。这个函数不仅简化了这个项的重复计算，而且也是我们在时间步长为隐式时使用的非线性迭代求解器的基本组成部分（即 $\theta\ne 0$  ）。此外，我们必须允许该函数接收一个 "旧 "和一个 "新 "的解决方案作为输入。这些可能不是存储在 old_solution and solution 中的问题的实际解决方案，而只是我们线性化的两个函数。为了这个函数的目的，让我们在下面这个类的文档中分别调用前两个参数  $w_{\mathrm{old}}$  和  $w_{\mathrm{new}}$  。

// 作为一个旁注，也许值得研究一下什么阶次的正交公式最适合这种类型的积分。由于 $\sin(\cdot)$ 不是一个多项式，可能没有正交公式可以准确地积分这些项。通常只需确保右手边的积分达到与离散化方案相同的精度即可，但通过选择更精确的正交公式，也许可以改善渐近收敛声明中的常数。

  template <int dim>
  void SineGordonProblem<dim>::compute_nl_term(const Vector<double> &old_data,
                                               const Vector<double> &new_data,
                                               Vector<double> &nl_term) const
  {
    nl_term = 0;
    const QGauss<dim> quadrature_formula(fe.degree + 1);
    FEValues<dim>     fe_values(fe,
                            quadrature_formula,
                            update_values | update_JxW_values |
                              update_quadrature_points);

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

    Vector<double>                       local_nl_term(dofs_per_cell);
    std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
    std::vector<double>                  old_data_values(n_q_points);
    std::vector<double>                  new_data_values(n_q_points);

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

// 一旦我们将 FEValues 实例化重新初始化到当前单元格，我们就利用 get_function_values 例程来获取 "旧 "数据（大概在 $t=t_{n-1}$ ）和 "新 "数据（大概在 $t=t_n$ ）在所选正交公式节点的值。

        fe_values.reinit(cell);
        fe_values.get_function_values(old_data, old_data_values);
        fe_values.get_function_values(new_data, new_data_values);

// 现在，我们可以用所需的正交公式来评估  $\int_K \sin\left[\theta w_{\mathrm{new}} + (1-\theta) w_{\mathrm{old}}\right] \,\varphi_j\,\mathrm{d}x$  。

        for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
          for (unsigned int i = 0; i < dofs_per_cell; ++i)
            local_nl_term(i) +=
              (std::sin(theta * new_data_values[q_point] +
                        (1 - theta) * old_data_values[q_point]) *
               fe_values.shape_value(i, q_point) * fe_values.JxW(q_point));

// 我们通过将各单元的积分对全局积分的贡献相加来得出结论。

        cell->get_dof_indices(local_dof_indices);

        for (unsigned int i = 0; i < dofs_per_cell; ++i)
          nl_term(local_dof_indices[i]) += local_nl_term(i);
      }
  }
// @sect4{SineGordonProblem::compute_nl_matrix}

// 这是处理非线性方案的第二个函数。它计算矩阵  $N(\cdot,\cdot)$  ，它出现在  $F(\cdot)$  的雅各布项的非线性项中。正如 compute_nl_term 一样，我们必须让这个函数接收一个 "旧 "和一个 "新 "的解决方案作为输入，我们再次将其分别称为 $w_{\mathrm{old}}$ 和 $w_{\mathrm{new}}$ ，如下。

  template <int dim>
  void SineGordonProblem<dim>::compute_nl_matrix(
    const Vector<double> &old_data,
    const Vector<double> &new_data,
    SparseMatrix<double> &nl_matrix) const
  {
    QGauss<dim>   quadrature_formula(fe.degree + 1);
    FEValues<dim> fe_values(fe,
                            quadrature_formula,
                            update_values | update_JxW_values |
                              update_quadrature_points);

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

    FullMatrix<double> local_nl_matrix(dofs_per_cell, dofs_per_cell);
    std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
    std::vector<double>                  old_data_values(n_q_points);
    std::vector<double>                  new_data_values(n_q_points);

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

// 同样，首先我们将我们的 FEValues 实例化重新初始化为当前单元。

        fe_values.reinit(cell);
        fe_values.get_function_values(old_data, old_data_values);
        fe_values.get_function_values(new_data, new_data_values);

// 然后，我们用所需的正交公式评估 $\int_K \cos\left[\theta w_{\mathrm{new}} + (1-\theta) w_{\mathrm{old}}\right]\, \varphi_i\, \varphi_j\,\mathrm{d}x$ 。

        for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
          for (unsigned int i = 0; i < dofs_per_cell; ++i)
            for (unsigned int j = 0; j < dofs_per_cell; ++j)
              local_nl_matrix(i, j) +=
                (std::cos(theta * new_data_values[q_point] +
                          (1 - theta) * old_data_values[q_point]) *
                 fe_values.shape_value(i, q_point) *
                 fe_values.shape_value(j, q_point) * fe_values.JxW(q_point));

// 最后，我们将各单元的积分对全局积分的贡献相加。

        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)
            nl_matrix.add(local_dof_indices[i],
                          local_dof_indices[j],
                          local_nl_matrix(i, j));
      }
  }

//  @sect4{SineGordonProblem::solve}

// 正如在介绍中所讨论的，这个函数在线性方程组上使用CG迭代求解器，该方程组是由牛顿方法的每个迭代的有限元空间离散化产生的，用于分割公式中的（非线性）第一个方程。该系统的解实际上是 $\delta U^{n,l}$ ，所以它被存储在 solution_update and used to update solution 的 run 函数中。

// 注意，我们在求解前将解的更新值重新设置为零。这是没有必要的：迭代求解器可以从任何一点开始并收敛到正确的解。如果对线性系统的解有一个很好的估计，那么从这个向量开始可能是值得的，但是作为一个一般的观察，起点并不是很重要：它必须是一个非常非常好的猜测，以减少超过几个迭代的次数。事实证明，对于这个问题，使用之前的非线性更新作为起点实际上会损害收敛性并增加所需的迭代次数，所以我们简单地将其设置为零。

// 该函数返回收敛到一个解决方案所需的迭代次数。这个数字以后将被用来在屏幕上生成输出，显示每次非线性迭代需要多少次迭代。

  template <int dim>
  unsigned int SineGordonProblem<dim>::solve()
  {
    SolverControl            solver_control(1000, 1e-12 * system_rhs.l2_norm());
    SolverCG<Vector<double>> cg(solver_control);

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

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

    return solver_control.last_step();
  }
// @sect4{SineGordonProblem::output_results}

// 这个函数将结果输出到一个文件。它与  step-23  和  step-24  中的相应函数基本相同。

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

    data_out.attach_dof_handler(dof_handler);
    data_out.add_data_vector(solution, "u");
    data_out.build_patches();

    const std::string filename =
      "solution-" + Utilities::int_to_string(timestep_number, 3) + ".vtu";
    DataOutBase::VtkFlags vtk_flags;
    vtk_flags.compression_level =
      DataOutBase::VtkFlags::ZlibCompressionLevel::best_speed;
    data_out.set_flags(vtk_flags);
    std::ofstream output(filename);
    data_out.write_vtu(output);
  }
// @sect4{SineGordonProblem::run}

// 这个函数对一切都有最高级别的控制：它运行（外部）时间步长循环，（内部）非线性求解器循环，并在每个时间步长后输出解。

  template <int dim>
  void SineGordonProblem<dim>::run()
  {
    make_grid_and_dofs();

// 为了确认初始条件，我们必须使用函数  $u_0(x)$  来计算  $U^0$  。为此，下面我们将创建一个 InitialValues 类型的对象；注意，当我们创建这个对象（它来自 Function 类）时，我们将其内部的时间变量设置为 $t_0$ ，以表明初始条件是在 $t=t_0$ 处评估的空间和时间的函数。

// 然后我们通过使用 VectorTools::project 将 $u_0(x)$ 投影到网格上，产生 $U^0$ 。我们必须使用与 step-21 相同的悬挂节点约束结构： VectorTools::project 函数需要一个悬挂节点约束对象，但为了使用它，我们首先需要关闭它。

    {
      AffineConstraints<double> constraints;
      constraints.close();
      VectorTools::project(dof_handler,
                           constraints,
                           QGauss<dim>(fe.degree + 1),
                           InitialValues<dim>(1, time),
                           solution);
    }

// 为了完整起见，我们像其他时间步长一样，将第2个时间步长输出到一个文件。

    output_results(0);

// 现在我们进行时间步进：在每个时间步进中，我们解决与问题的有限元离散化相对应的矩阵方程，然后根据我们在介绍中讨论的时间步进公式推进我们的解决方案。

    unsigned int timestep_number = 1;
    for (time += time_step; time <= final_time;
         time += time_step, ++timestep_number)
      {
        old_solution = solution;

        std::cout << std::endl
                  << "Time step #" << timestep_number << "; "
                  << "advancing to t = " << time << "." << std::endl;

// 在每个时间步长的开始，我们必须通过牛顿方法求解拆分公式中的非线性方程---即先求解 $\delta U^{n,l}$ ，然后再计算 $U^{n,l+1}$ ，如此反复。这种非线性迭代的停止标准是： $\|F_h(U^{n,l})\|_2 \le 10^{-6} \|F_h(U^{n,0})\|_2$  。因此，我们需要记录第一次迭代中残差的规范。

// 在每次迭代结束时，我们向控制台输出我们花了多少次线性求解器的迭代。当下面的循环完成后，我们有（一个近似的） $U^n$  。

        double initial_rhs_norm = 0.;
        bool   first_iteration  = true;
        do
          {
            assemble_system();

            if (first_iteration == true)
              initial_rhs_norm = system_rhs.l2_norm();

            const unsigned int n_iterations = solve();

            solution += solution_update;

            if (first_iteration == true)
              std::cout << "    " << n_iterations;
            else
              std::cout << '+' << n_iterations;
            first_iteration = false;
          }
        while (system_rhs.l2_norm() > 1e-6 * initial_rhs_norm);

        std::cout << " CG iterations per nonlinear step." << std::endl;

// 在得到问题的第一个方程 $t=t_n$ 的解后，我们必须更新辅助速度变量  $V^n$  。然而，我们不计算和存储 $V^n$ ，因为它不是我们在问题中直接使用的数量。因此，为了简单起见，我们直接更新 $MV^n$ 。

        Vector<double> tmp_vector(solution.size());
        laplace_matrix.vmult(tmp_vector, solution);
        M_x_velocity.add(-time_step * theta, tmp_vector);

        laplace_matrix.vmult(tmp_vector, old_solution);
        M_x_velocity.add(-time_step * (1 - theta), tmp_vector);

        compute_nl_term(old_solution, solution, tmp_vector);
        M_x_velocity.add(-time_step, tmp_vector);

// 很多时候，特别是对于细网格，我们必须选择相当小的时间步长，以使方案稳定。因此，有很多时间步长，在解的过程中 "没有什么有趣的事情发生"。为了提高整体效率--特别是加快程序速度和节省磁盘空间--我们每隔 output_timestep_skip 个时间步数才输出解。

        if (timestep_number % output_timestep_skip == 0)
          output_results(timestep_number);
      }
  }
} // namespace Step25
// @sect3{The main function}

// 这是该程序的主函数。它创建一个顶层类的对象并调用其主函数。如果在执行 SineGordonProblem 类的运行方法时抛出了异常，我们会在这里捕获并报告它们。关于异常的更多信息，读者应该参考  step-6  。

int main()
{
  try
    {
      using namespace Step25;

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

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

  return 0;
}


// see also <http://web.mit.edu/fwtools_v3.1.0/www/H5.intro.html>

#include <string>
#include <memory>
#include <Eigen/Dense>
#include <H5Cpp.h>

#include <iostream>

using namespace H5;
const H5std_string FILE_NAME("states.h5");
const H5std_string DATASET_NAME("States");
PredType dType {PredType::NATIVE_DOUBLE};
typedef double data_t;

const int FSPACE_RANK = 2; // Dataset rank as it is stored in the file
const int FSPACE_DIM1 = 5; // Dimension sizes of the dataset as it is
const int FSPACE_DIM2 = 3; // stored in the file

const int MSPACE_RANK = 1; // Rank of dataset in memory
const int MSPACE_DIM = FSPACE_DIM1 * FSPACE_DIM2; // Dataset size in memory

void readfile();
void writefile();
void testEigen();
Eigen::MatrixXd mk_mat();

using std::cout;
using std::endl;

using RowMatrixXd = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;

int main()
{
    cout << "> Test Eigen..." << endl;
    Eigen::MatrixXd m {mk_mat()};
    cout << "MatrixXd @ " << m.data() << ":\n" << m << endl;

    cout << "> Write data to " << FILE_NAME << "..." << endl;
    writefile();

    cout << "> Read data from " << FILE_NAME << "..." << endl;
    readfile();

    return 0;
}

Eigen::MatrixXd mk_mat()
{
    Eigen::MatrixXd m(FSPACE_DIM1, FSPACE_DIM2);
    int c = 0;
    for (int i = 0; i < FSPACE_DIM1; ++i)
        for (int j = 0; j < FSPACE_DIM2; ++j)
        {
            ++c;
            m(i,j) = c;
        }

    cout << "MatrixXd @ " << m.data() << ":\n" << m << endl;
    return m;
}

void testEigen()
{
    Eigen::MatrixXd m(FSPACE_DIM1, FSPACE_DIM2);
    int c = 0;
    for (int i = 0; i < FSPACE_DIM1; ++i)
        for (int j = 0; j < FSPACE_DIM2; ++j)
        {
            ++c;
            m(i,j) = c;
        }

    cout << "MatrixXd:\n" << m << endl;
}

void writefile()
{
    H5File file(FILE_NAME, H5F_ACC_TRUNC);
    // Create dataspace for the dataset in the file
    hsize_t fdim[] = {FSPACE_DIM1, FSPACE_DIM2};
    DataSpace fspace(FSPACE_RANK, fdim);
    // Create dataset and write it into the file
    DataSet dataset(file.createDataSet(DATASET_NAME, dType, fspace));
    // Select hyperslab for the dataset in the file
    hsize_t start[2] = {0, 0}; // Start of hyperslab
    hsize_t stride[2] = {FSPACE_DIM1, FSPACE_DIM2}; // Stride of hyperslab
    hsize_t block[2] = {FSPACE_DIM1, FSPACE_DIM2};  // Block sizes
    hsize_t count[2] = {1, 1};  // Block count

    hsize_t mdim[] = {MSPACE_DIM};  // Dimension size of the first dataset
    DataSpace mspace(MSPACE_RANK, mdim);
    // data
    Eigen::MatrixXd m(FSPACE_DIM1, FSPACE_DIM2);
    int c = 0;
    for (int i = 0; i < FSPACE_DIM1; ++i)
        for (int j = 0; j < FSPACE_DIM2; ++j)
        {
            ++c;
            m(i,j) = c;
        }

    cout << "MatrixXd m:\n" << m << endl;
    data_t* data {m.data()};

    cout << "MatrixXd internal storage order:" << endl;
    for (c = 0; c < MSPACE_DIM; ++c)
        cout << data[c] << ", ";
    cout << endl;

    fspace.selectHyperslab(H5S_SELECT_SET, count, start, stride, block);
    dataset.write(m.data(), dType, mspace, fspace);

    // Reset the selection for the file dataspace
    fspace.selectNone();
}

void readfile()
{
    // Open the file
    H5File file(FILE_NAME, H5F_ACC_RDONLY);
    // Open the dataset.
    DataSet dataset(file.openDataSet(DATASET_NAME));
    // Get dataspace of the dataset
    DataSpace fspace {dataset.getSpace()};
    int rank = fspace.getSimpleExtentNdims();
    hsize_t* fdims = new hsize_t[rank];
    fspace.getSimpleExtentDims(fdims);

    cout << "r/rank = " << rank << endl;
    cout << "r/dims = ";
    int d = 0;
    for (; d < rank; ++d)
        cout << fdims[d] << ", ";
    const hsize_t n_rows = fdims[0], n_cols = fdims[1];
    delete [] fdims;

    // Read data into a vector
    hsize_t mdim[] = {n_rows * n_cols};
    DataSpace mspace(MSPACE_RANK, mdim);

    Eigen::MatrixXd mat(n_rows, n_cols);
    dataset.read(mat.data(), dType, mspace, fspace);
    cout << "MatrixXd mat:\n" << mat << endl;
}


//
// MIT License
//
// Copyright (c) Deif Lou
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//

#include <Eigen/Sparse>

#include "cubicsplineinterpolator1D.h"

namespace ibp {
namespace misc {

CubicSplineInterpolator1D::CubicSplineInterpolator1D() :
    BaseSplineInterpolator1D(),
    mFloorBoundaryConditions(BoundaryConditions_Natural),
    mCeilBoundaryConditions(BoundaryConditions_Natural),
    mFloorBoundaryConditionsValue(0.),
    mCeilBoundaryConditionsValue(0.),
    mIsDirty(true)
{
}

Interpolator1D * CubicSplineInterpolator1D::clone() const
{
    CubicSplineInterpolator1D * si = new CubicSplineInterpolator1D();
    if (!si)
        return 0;
    si->mKnots = mKnots;
    si->mFloorExtrapolationMode = mFloorExtrapolationMode;
    si->mCeilExtrapolationMode = mCeilExtrapolationMode;
    si->mFloorExtrapolationValue = mFloorExtrapolationValue;
    si->mCeilExtrapolationValue = mCeilExtrapolationValue;
    si->mFloorBoundaryConditions = mFloorBoundaryConditions;
    si->mCeilBoundaryConditions = mCeilBoundaryConditions;
    si->mFloorBoundaryConditionsValue = mFloorBoundaryConditionsValue;
    si->mCeilBoundaryConditionsValue = mCeilBoundaryConditionsValue;
    si->mCoefficients = mCoefficients;
    si->mIsDirty = mIsDirty;
    return si;
}

double CubicSplineInterpolator1D::F(double x)
{
    if (mIsDirty)
        calculateCoefficients();
    const int piece = pieceForValue(x);
    const double w = x - mKnots[piece].x();
    return ((mCoefficients[piece].a * w + mCoefficients[piece].b) * w +
            mCoefficients[piece].c) * w + mCoefficients[piece].d;
}

double CubicSplineInterpolator1D::floorExtrapolate(double x)
{
    if (mFloorExtrapolationMode == ExtrapolationMode_Constant)
        return mFloorExtrapolationValue;
    else if (mFloorExtrapolationMode == ExtrapolationMode_FollowTangent && mKnots.size() > 1)
    {
        if (mIsDirty)
            calculateCoefficients();
        return mCoefficients.first().c * (x - mKnots[0].x()) + mKnots[0].y();
    }
    else if (mFloorExtrapolationMode == ExtrapolationMode_Repeat && mKnots.size() > 1)
        x = fmod(x - mKnots.first().x(), mKnots.last().x() - mKnots.first().x()) + mKnots.last().x();
    else if (mFloorExtrapolationMode == ExtrapolationMode_Mirror && mKnots.size() > 1)
    {
        const double a = (mKnots.last().x() - mKnots.first().x()) * 2.;
        const double b = (x - mKnots.first().x()) / a;
        x = fabs(b - floor(b + .5)) * a + mKnots.first().x();
    }
    else
        return mKnots.first().y();
    return F(x);
}

double CubicSplineInterpolator1D::ceilExtrapolate(double x)
{
    if (mCeilExtrapolationMode == ExtrapolationMode_Constant)
        return mCeilExtrapolationValue;
    else if (mCeilExtrapolationMode == ExtrapolationMode_FollowTangent && mKnots.size() > 1)
    {
        if (mIsDirty)
            calculateCoefficients();
        const double lkp = mKnots.last().x() - mKnots[mKnots.size() - 2].x();
        return ((3. * mCoefficients.last().a * lkp + 2. * mCoefficients.last().b) * lkp + mCoefficients.last().c) *
               (x - mKnots.last().x()) + mKnots.last().y();
    }
    else if (mCeilExtrapolationMode == ExtrapolationMode_Repeat && mKnots.size() > 1)
        x = fmod(x - mKnots.first().x(), mKnots.last().x() - mKnots.first().x()) + mKnots.first().x();
    else if (mCeilExtrapolationMode == ExtrapolationMode_Mirror && mKnots.size() > 1)
    {
        const double a = (mKnots.last().x() - mKnots.first().x()) * 2.;
        const double b = (x - mKnots.first().x()) / a;
        x = fabs(b - floor(b + .5)) * a + mKnots.first().x();
    }
    else
        return mKnots.last().y();
    return F(x);
}

void CubicSplineInterpolator1D::calculateCoefficients()
{
    if (mKnots.size() < 1) return;

    register int kSize = mKnots.size();
    bool isPeriodic = mFloorBoundaryConditions == BoundaryConditions_Periodic &&
                      mCeilBoundaryConditions == BoundaryConditions_Periodic;

    mCoefficients.clear();
    mCoefficients.resize(kSize > 1 ? kSize - 1 : 1);

    if (kSize == 1)
    {
        // ------------------------------------------
        // constant, the final value is extrapolated
        // from the unique knot
        // ------------------------------------------
        mCoefficients[0].a = mCoefficients[0].b = mCoefficients[0].c = 0.;
        mCoefficients[0].d = mKnots[0].y();
    }
    else if (kSize == 2)
    {
        // ------------------------------------------
        // linear interpolation
        // ------------------------------------------
        mCoefficients[0].a = mCoefficients[0].b = 0.;
        mCoefficients[0].c = (mKnots[1].y() - mKnots[0].y()) / (mKnots[1].x() - mKnots[0].x());
        mCoefficients[0].d = mKnots[0].y();
    }
    else
    {
        // ------------------------------------------
        // cubic interpolation
        // ------------------------------------------
        std::vector<Eigen::Triplet<double> > tripletList;
        Eigen::SparseMatrix<double> AMatrix;
        Eigen::VectorXd bVector;
        Eigen::VectorXd xVector;
        register float dx0, dx1, dx2, dy0, dy1;

        // ------------------------------------------
        // if is periodic, we need to solve a
        // (ksize - 1) x (ksize - 1) system; otherwise,
        // the system is (ksize - 2) x (ksize - 2)
        // ------------------------------------------
        if (isPeriodic)
        {
            tripletList.reserve((kSize - 1) * 3);
            bVector = Eigen::VectorXd(kSize - 1);

            // ------------------------------------------
            // fill the A matrix and the b vector
            // ------------------------------------------
            for (int i = 1; i < kSize - 2; i++)
            {
                // distances between knots
                dx0 = mKnots[i].x() - mKnots[i - 1].x();
                dx1 = mKnots[i + 1].x() - mKnots[i].x();
                dx2 = dx0 + dx1;
                dy0 = mKnots[i].y() - mKnots[i - 1].y();
                dy1 = mKnots[i + 1].y() - mKnots[i].y();
                // diagonal
                tripletList.push_back(Eigen::Triplet<double>(i, i, 2. * dx2));
                // upper diagonal
                tripletList.push_back(Eigen::Triplet<double>(i, i + 1, dx1));
                // lower diagonal
                tripletList.push_back(Eigen::Triplet<double>(i + 1, i, dx1));
                // b vector
                bVector(i) = 6. * (dy1 / dx1 - dy0 / dx0);
            }

            // ------------------------------------------
            // fill the (0, 0), (0, 1), (0, n - 1),
            // (1, 0), (n - 1, 0), (n - 1, n - 1) entries
            // of A and the 0 and n - 1 entries of b
            // ------------------------------------------
            dx0 = mKnots[kSize - 1].x() - mKnots[kSize - 2].x();
            dx1 = mKnots[1].x() - mKnots[0].x();
            dx2 = dx0 + dx1;
            dy0 = mKnots[0].y() - mKnots[kSize - 2].y();
            dy1 = mKnots[1].y() - mKnots[0].y();
            tripletList.push_back(Eigen::Triplet<double>(0, 0, 2. * dx2));
            tripletList.push_back(Eigen::Triplet<double>(0, 1, dx1));
            tripletList.push_back(Eigen::Triplet<double>(0, kSize - 2, dx0));
            tripletList.push_back(Eigen::Triplet<double>(1, 0, dx1));
            bVector(0) = 6. * (dy1 / dx1 - dy0 / dx0);
            dx0 = mKnots[kSize - 2].x() - mKnots[kSize - 3].x();
            dx1 = mKnots[kSize - 1].x() - mKnots[kSize - 2].x();
            dx2 = dx0 + dx1;
            dy0 = mKnots[kSize - 2].y() - mKnots[kSize - 3].y();
            dy1 = mKnots[0].y() - mKnots[kSize - 2].y();
            tripletList.push_back(Eigen::Triplet<double>(kSize - 2, 0, dx1));
            tripletList.push_back(Eigen::Triplet<double>(kSize - 2, kSize - 2, 2. * dx2));
            bVector(kSize - 2) = 6. * (dy1 / dx1 - dy0 / dx0);

            // ------------------------------------------
            // solve system and copy solution to vector x
            // ------------------------------------------
            AMatrix = Eigen::SparseMatrix<double>(kSize - 1, kSize - 1);
            AMatrix.setFromTriplets(tripletList.begin(), tripletList.end());
            Eigen::SparseLU<Eigen::SparseMatrix<double> > solver(AMatrix);
            xVector = solver.solve(bVector);
        }
        else
        {
            tripletList.reserve((kSize - 2) * 3 - 2);
            bVector = Eigen::VectorXd(kSize - 2);
            xVector = Eigen::VectorXd(kSize);

            // ------------------------------------------
            // fill the A matrix and the b vector
            // ------------------------------------------
            for (int i = 0; i < kSize - 2; i++)
            {
                // distances between knots
                dx0 = mKnots[i + 1].x() - mKnots[i].x();
                dx1 = mKnots[i + 2].x() - mKnots[i + 1].x();
                dx2 = dx0 + dx1;
                dy0 = mKnots[i + 1].y() - mKnots[i].y();
                dy1 = mKnots[i + 2].y() - mKnots[i + 1].y();
                // diagonal
                tripletList.push_back(Eigen::Triplet<double>(i, i, 2. * dx2));
                if (i < kSize - 3)
                {
                    // upper diagonal
                    tripletList.push_back(Eigen::Triplet<double>(i, i + 1, dx1));
                    // lower diagonal
                    tripletList.push_back(Eigen::Triplet<double>(i + 1, i, dx1));
                }
                // b vector
                bVector(i) = 6. * (dy1 / dx1 - dy0 / dx0);
            }

            // ------------------------------------------
            // modify A matrix and b vector subject to
            // the end point constraints
            // ------------------------------------------
            dx0 = mKnots[1].x() - mKnots[0].x();
            dx1 = mKnots[kSize - 1].x() - mKnots[kSize - 2].x();
            dy0 = mKnots[1].y() - mKnots[0].y();
            dy1 = mKnots[kSize - 1].y() - mKnots[kSize - 2].y();
            if (mFloorBoundaryConditions == BoundaryConditions_Fixed1stDerivatives)
            {
                bVector(0) -= 3. * (dy0 / dx0 - mFloorBoundaryConditionsValue);
                tripletList.at(0) = Eigen::Triplet<double>(0, 0, tripletList.at(0).value() - dx0 / 2.);
            }
            else if (mFloorBoundaryConditions == BoundaryConditions_Fixed2ndDerivatives)
                bVector(0) -= dx0 * mFloorBoundaryConditionsValue;
            else if (mFloorBoundaryConditions == BoundaryConditions_Copy)
                tripletList.at(0) = Eigen::Triplet<double>(0, 0, tripletList.at(0).value() + dx0);
            else if (mFloorBoundaryConditions == BoundaryConditions_Extrapolate && kSize > 3)
            {
                dx2 = dx0 * dx0 / (mKnots[2].x() - mKnots[1].x());
                tripletList.at(0) = Eigen::Triplet<double>(0, 0, tripletList.at(0).value() + dx0 + dx2);
                tripletList.at(1) = Eigen::Triplet<double>(0, 1, tripletList.at(1).value() - dx2);
            }
            // EndPointConstraints_Natural, EndPointConstraints_Extrapolate && kSize == 3
            else
                xVector(0) = 0.;

            if (mCeilBoundaryConditions == BoundaryConditions_Fixed1stDerivatives)
            {
                bVector(kSize - 3) -= 3. * (mCeilBoundaryConditionsValue - dy1 / dx1);
                tripletList.at(tripletList.size() - 1) = Eigen::Triplet<double>(kSize - 3, kSize - 3,
                                                         tripletList.at(tripletList.size() - 1).value() - dx1 / 2.);
            }
            else if (mCeilBoundaryConditions == BoundaryConditions_Fixed2ndDerivatives)
                bVector(kSize - 3) -= dx1 * mCeilBoundaryConditionsValue;
            else if (mCeilBoundaryConditions == BoundaryConditions_Copy)
                tripletList.at(tripletList.size() - 1) = Eigen::Triplet<double>(kSize - 3, kSize - 3,
                                                         tripletList.at(tripletList.size() - 1).value() + dx1);
            else if (mCeilBoundaryConditions == BoundaryConditions_Extrapolate && kSize > 3)
            {
                dx2 = dx1 * dx1 / (mKnots[kSize - 2].x() - mKnots[kSize - 3].x());
                tripletList.at(tripletList.size() - 1) = Eigen::Triplet<double>(kSize - 3, kSize - 3,
                                                         tripletList.at(tripletList.size() - 1).value() + dx1 + dx2);
                tripletList.at(tripletList.size() - 2) = Eigen::Triplet<double>(kSize - 3, kSize - 4,
                                                         tripletList.at(tripletList.size() - 2).value() - dx2);
            }
            // EndPointConstraints_Natural, EndPointConstraints_Extrapolate && kSize == 3
            else
                xVector(kSize - 1) = 0.;

            // ------------------------------------------
            // solve system and copy solution to vector x
            // ------------------------------------------
            AMatrix = Eigen::SparseMatrix<double>(kSize - 2, kSize - 2);
            AMatrix.setFromTriplets(tripletList.begin(), tripletList.end());
            Eigen::SparseLU<Eigen::SparseMatrix<double> > solver(AMatrix);
            Eigen::VectorXd xVector2 = solver.solve(bVector);
            for (int i = 0; i < kSize - 2; i++)
                xVector(i + 1) = xVector2(i);

            // ------------------------------------------
            // fix the values of the second derivatives
            // at the first and last knots based on
            // the computed second derivatives at the
            // interiot knots
            // ------------------------------------------
            if (mFloorBoundaryConditions == BoundaryConditions_Fixed1stDerivatives)
                xVector(0) = (3. / dx0) * (dy0 / dx0 - mFloorBoundaryConditionsValue) - (xVector(1) / 2.);
            else if (mFloorBoundaryConditions == BoundaryConditions_Fixed2ndDerivatives)
                xVector(0) = mFloorBoundaryConditionsValue;
            else if (mFloorBoundaryConditions == BoundaryConditions_Copy)
                xVector(0) = xVector(1);
            else if (mFloorBoundaryConditions == BoundaryConditions_Extrapolate && kSize > 3)
                xVector(0) = xVector(1) - dx0 * (xVector(2) - xVector(1)) / (mKnots[2].x() - mKnots[1].x());

            if (mCeilBoundaryConditions == BoundaryConditions_Fixed1stDerivatives)
                xVector(kSize - 1) = (3. / dx1) * (mCeilBoundaryConditionsValue - dy1 / dx1) -
                        (xVector(kSize - 2) / 2.);
            else if (mCeilBoundaryConditions == BoundaryConditions_Fixed2ndDerivatives)
                xVector(kSize - 1) = mCeilBoundaryConditionsValue;
            else if (mCeilBoundaryConditions == BoundaryConditions_Copy)
                xVector(kSize - 1) = xVector(kSize - 2);
            else if (mCeilBoundaryConditions == BoundaryConditions_Extrapolate && kSize > 3)
                xVector(kSize - 1) = xVector(kSize - 2) + dx1 * (xVector(kSize - 2) - xVector(kSize - 3)) /
                                     (mKnots[kSize - 2].x() - mKnots[kSize - 3].x());
        }

        // ------------------------------------------
        // compute polynomial coefficients
        // ------------------------------------------
        for (int i = 0; i < kSize - 1; i++)
        {
            // distances between knots
            dx1 = mKnots[i + 1].x() - mKnots[i].x();
            dy1 = mKnots[i + 1].y() - mKnots[i].y();
            // compute coefficients
            dx2 = isPeriodic && i == kSize - 2 ? xVector(0) : xVector(i + 1);
            mCoefficients[i].a = (dx2 - xVector(i)) / (6. * dx1);
            mCoefficients[i].b = xVector(i) / 2.;
            mCoefficients[i].c = dy1 / dx1 - dx1 * (2. * xVector(i) + dx2) / 6.;
            mCoefficients[i].d = mKnots[i].y();
        }
    }

    mIsDirty = false;
}

bool CubicSplineInterpolator1D::setKnots(const Interpolator1DKnots &k)
{
    bool b = BaseSplineInterpolator1D::setKnots(k);
    if (b) mIsDirty = true;
    return b;
}

bool CubicSplineInterpolator1D::setKnot(int i, const Interpolator1DKnot &k)
{
    bool b = BaseSplineInterpolator1D::setKnot(i, k);
    if (b) mIsDirty = true;
    return b;
}

bool CubicSplineInterpolator1D::setKnot(int i, double nx, double ny)
{
    bool b = BaseSplineInterpolator1D::setKnot(i, nx, ny);
    if (b) mIsDirty = true;
    return b;
}

bool CubicSplineInterpolator1D::setKnot(double x, const Interpolator1DKnot &k)
{
    bool b = BaseSplineInterpolator1D::setKnot(x, k);
    if (b) mIsDirty = true;
    return b;
}

bool CubicSplineInterpolator1D::setKnot(double x, double nx, double ny)
{
    bool b = BaseSplineInterpolator1D::setKnot(x, nx, ny);
    if (b) mIsDirty = true;
    return b;
}

bool CubicSplineInterpolator1D::addKnot(const Interpolator1DKnot &k, bool replace, int * index)
{
    bool b = BaseSplineInterpolator1D::addKnot(k, replace, index);
    if (b) mIsDirty = true;
    return b;
}

bool CubicSplineInterpolator1D::addKnot(double nx, double ny, bool replace, int * index)
{
    bool b = BaseSplineInterpolator1D::addKnot(nx, ny, replace, index);
    if (b) mIsDirty = true;
    return b;
}

bool CubicSplineInterpolator1D::removeKnot(double x)
{
    bool b = BaseSplineInterpolator1D::removeKnot(x);
    if (b) mIsDirty = true;
    return b;
}

bool CubicSplineInterpolator1D::removeKnot(int i)
{
    bool b = BaseSplineInterpolator1D::removeKnot(i);
    if (b) mIsDirty = true;
    return b;
}

CubicSplineInterpolator1D::BoundaryConditions CubicSplineInterpolator1D::floorBoundaryConditions() const
{
    return mFloorBoundaryConditions;
}

CubicSplineInterpolator1D::BoundaryConditions CubicSplineInterpolator1D::ceilBoundaryConditions() const
{
    return mCeilBoundaryConditions;
}

double CubicSplineInterpolator1D::floorBoundaryConditionsValue() const
{
    return mFloorBoundaryConditionsValue;
}

double CubicSplineInterpolator1D::ceilBoundaryConditionsValue() const
{
    return mCeilBoundaryConditionsValue;
}

void CubicSplineInterpolator1D::setBoundaryConditions(BoundaryConditions f, BoundaryConditions c,
                                                     double fv, double cv)
{
    mFloorBoundaryConditions = f;
    mCeilBoundaryConditions = c;
    mFloorBoundaryConditionsValue = fv;
    mCeilBoundaryConditionsValue = cv;
    mIsDirty = true;
}

}}


#define BOOST_TEST_MODULE test_fm

#include <boost/test/unit_test.hpp>
#include <smtrat-common/smtrat-common.h>
#include <smtrat-qe/fm/qe.h>
#include <iostream>

using namespace smtrat;

BOOST_AUTO_TEST_SUITE( FMQE );

BOOST_AUTO_TEST_CASE( FMQE_eliminate_single_variable )
{
    carl::Variable x = carl::freshRealVariable("x");
    carl::Variable y = carl::freshRealVariable("y");
    carl::Variable z = carl::freshRealVariable("z");

    ConstraintT c1 = ConstraintT(Poly(x) - Poly(y) + Poly(z), carl::Relation::GEQ);
    ConstraintT c2 = ConstraintT(Poly(x) + Poly(y) + Poly(-5), carl::Relation::LEQ);

    FormulasT constraints;
    constraints.emplace_back(c1);
    constraints.emplace_back(c2);

    FormulaT inFormula = FormulaT(carl::FormulaType::AND, constraints);

    std::cout << "Formula: " << inFormula << ", eliminate " << x << std::endl;

    qe::QEQuery query;
    query.emplace_back(std::make_pair(qe::QuantifierType::EXISTS,std::vector<carl::Variable>{x}));

    auto newFormula = qe::fm::eliminateQuantifiers(inFormula, query);

    std::cout << "New formula: " << newFormula << std::endl;

    BOOST_TEST(true, "Ran successfully.");
}

BOOST_AUTO_TEST_CASE( FMQE_eliminate_to_true )
{
    carl::Variable x = carl::freshRealVariable("x");
    carl::Variable y = carl::freshRealVariable("y");

    ConstraintT c1 = ConstraintT(Poly(x) - Poly(y), carl::Relation::GEQ);
    ConstraintT c2 = ConstraintT(Poly(x) - Poly(y) + Poly(-5), carl::Relation::LEQ);

    FormulasT constraints;
    constraints.emplace_back(c1);
    constraints.emplace_back(c2);

    FormulaT inFormula = FormulaT(carl::FormulaType::AND, constraints);

    std::cout << "Formula: " << inFormula << ", eliminate " << x << std::endl;

    qe::QEQuery query;
    query.emplace_back(std::make_pair(qe::QuantifierType::EXISTS,std::vector<carl::Variable>{x}));

    auto newFormula = qe::fm::eliminateQuantifiers(inFormula, query);

    std::cout << "New formula: " << newFormula << std::endl;

    BOOST_TEST((newFormula == FormulaT(carl::FormulaType::TRUE)));
}

BOOST_AUTO_TEST_CASE( FMQE_eliminate_to_true_with_remaining_constraint )
{
    carl::Variable x = carl::freshRealVariable("x");
    carl::Variable y = carl::freshRealVariable("y");
    carl::Variable z = carl::freshRealVariable("z");

    ConstraintT c1 = ConstraintT(Poly(x) - Poly(y), carl::Relation::GEQ);
    ConstraintT c2 = ConstraintT(Poly(x) - Poly(y) + Poly(-5), carl::Relation::LEQ);
    ConstraintT c3 = ConstraintT(Poly(y) - Poly(z), carl::Relation::LEQ);

    FormulasT constraints;
    constraints.emplace_back(c1);
    constraints.emplace_back(c2);
    constraints.emplace_back(c3);

    FormulaT inFormula = FormulaT(carl::FormulaType::AND, constraints);

    std::cout << "Formula: " << inFormula << ", eliminate " << x << std::endl;

    qe::QEQuery query;
    query.emplace_back(std::make_pair(qe::QuantifierType::EXISTS,std::vector<carl::Variable>{x}));

    auto newFormula = qe::fm::eliminateQuantifiers(inFormula, query);

    std::cout << "New formula: " << newFormula << std::endl;

    BOOST_TEST((newFormula == FormulaT(c3)));
}

BOOST_AUTO_TEST_CASE( FMQE_eliminate_several_variables )
{
    carl::Variable x = carl::freshRealVariable("x");
    carl::Variable y = carl::freshRealVariable("y");
    carl::Variable z = carl::freshRealVariable("z");

    ConstraintT c1 = ConstraintT(Poly(x) - Poly(y) + Poly(z), carl::Relation::GEQ);
    ConstraintT c2 = ConstraintT(Poly(y), carl::Relation::GEQ);
    ConstraintT c3 = ConstraintT(Poly(x) + Poly(y) + Poly(-5), carl::Relation::LEQ);

    FormulasT constraints;
    constraints.emplace_back(c1);
    constraints.emplace_back(c2);
    constraints.emplace_back(c3);

    FormulaT inFormula = FormulaT(carl::FormulaType::AND, constraints);

    std::cout << "Formula: " << inFormula << std::endl;

    qe::QEQuery query;
    query.emplace_back(std::make_pair(qe::QuantifierType::EXISTS,std::vector<carl::Variable>{x,y}));

    auto newFormula = qe::fm::eliminateQuantifiers(inFormula, query);

    std::cout << "New formula: " << newFormula << std::endl;

    BOOST_TEST(true, "Ran successfully.");
}

BOOST_AUTO_TEST_SUITE_END();


#include "../DynAutoDiff/CeresOptimizer.hpp"
#include "../DynAutoDiff/DynAutoDiff.hpp"
#include <algorithm>
#include <boost/test/tools/old/interface.hpp>
#include <eigen3/Eigen/Core>
#include <iostream>
#include <ostream>
#include <vector>

#define BOOST_TEST_MODULE Normal_Test
#include <boost/test/included/unit_test.hpp>
#include <boost/test/tools/floating_point_comparison.hpp>
#define TL 1e-10

using namespace Eigen;
using namespace std;
using namespace DynAutoDiff;

BOOST_AUTO_TEST_SUITE(Eigen_Helper_Test)

BOOST_AUTO_TEST_CASE(load_mat_test) {
    auto X = std::make_shared<Var<>>("X.txt");
    auto y = std::make_shared<Var<>>("y.txt");
    auto theta = pvec(X->cols()), c = psca();
    auto loss = mse_loss(X * theta + c, y);

    CeresOptimizer opt(loss);
    opt.run();
    std::cout << theta->val() << endl << c->v() << endl;
}

BOOST_AUTO_TEST_CASE(logistic_regression) {
    auto X = std::make_shared<Var<>>("Xb.txt");
    auto y = std::make_shared<Var<>>("yb.txt");
    auto theta = pvec(X->cols()), c = psca();
    auto loss = binary_cross_entropy(sigmoid(X * theta + c), y);

    CeresOptimizer opt(loss);
    opt.run();
    std::cout << theta->val() << endl << c->v() << endl;
}
BOOST_AUTO_TEST_SUITE_END()


#include <boost/python/module.hpp>
#include <boost/python/def.hpp>

namespace {

  long
  factorial(int n)
  {
    long fact = 1;
    for(int i=2;i<=n;i++) {
      fact *= i;
    }
    return fact;
  }

  double
  power(double x, int n)
  {
    double pow = x;
    for(int i=1;i<n;i++) {
      pow *= x;
    }
    return pow;
  }

  double
  sin(double x, int n_terms)
  {
    double result = x;
    int sign = -1;
    int pow = 3;
    for(int i=1;i<n_terms;i++) {
      result += power(x,pow)/(sign*factorial(pow));
      sign *= -1;
      pow += 2;
    }
    return result;
  }

  double
  run_c_plus_plus(int n, int n_terms)
  {
    double result = 0;
    while (n--) {
      for(int i=0;i<180;i++) {
        result += sin(i * 3.14159265359/180, n_terms);
      }
    }
    return result;
  }

} // namespace anonymous

BOOST_PYTHON_MODULE(boost_python_hybrid_times_ext)
{
  using namespace boost::python;
  def("run_c_plus_plus", run_c_plus_plus);
}


// Copyright András Vukics 2006–2020. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE.txt)
#define BOOST_TEST_MODULE StateVector
#include <boost/test/unit_test.hpp>

#include "NonOrthogonalStateVector.h"

using namespace quantumdata;

typedef CArray<1> Array1;
typedef CArray<2> Array2;
typedef NonOrthogonalStateVector<1, Array2> NOSV_Array2;
typedef transformation::Identity<1> I1;
typedef NonOrthogonalStateVector<1, I1> NOSV_I1;
typedef transformation::Identity<2> I2;
typedef NonOrthogonalStateVector<2, I2> NOSV_I2;

BOOST_AUTO_TEST_CASE(Dual)
{
    // Create Transformation Matrix
    Array2 T(3);
    T = 1,0,0,
        0,1,dcomp(0.5,-0.3),
        0,dcomp(0.5,0.3),1;

    // Create StateVector
    Array1 x(3);
    x = 1,2,3;
    NOSV_Array2 sv1(x,T,byReference);

    // Calculate and check dual vector
    sv1.update();
    Array1 dual = sv1.dual();
    BOOST_CHECK_EQUAL(dual(0), x(0)*T(0,0)+x(1)*T(0,1)+x(2)*T(0,2));
    BOOST_CHECK_EQUAL(dual(1), x(0)*T(1,0)+x(1)*T(1,1)+x(2)*T(1,2));
    BOOST_CHECK_EQUAL(dual(2), x(0)*T(2,0)+x(1)*T(2,1)+x(2)*T(2,2));

    // Check norm
    BOOST_CHECK_EQUAL(sv1.norm(), sqrt(real(
                                    sv1()(0)*conj(dual(0)) +
                                    sv1()(1)*conj(dual(1)) +
                                    sv1()(2)*conj(dual(2))
                                    )));
};

BOOST_AUTO_TEST_CASE(CompositeStateVector)
{
    // Create Transformation Matrix
    Array2 T(3,3);
    T = 1,0,0,
        0,1,dcomp(0.5,-0.5),
        0,dcomp(0.5,0.5),1;

    // Create StateVectors
    Array1 x(3);
    x = 1,2,3;
    NOSV_Array2 sv1(x,T,byReference);

    Array2 y(3,3);
    y = 1,2,
        3,1;
    I2 i2;
    NOSV_I2 sv2(y,i2,byReference);

    typedef TensorType<NOSV_I2, NOSV_Array2>::type Result;
    typedef Result::StateVectorLow ResultArray;
    Result res1(sv2*sv1);

    // Calculate all dual vectors.
    res1.update();
    sv1.update();
    sv2.update();

    // Check Composite StateVector
    ResultArray res2(blitzplusplus::doDirect(sv2.dual(), sv1.dual(),
                                    blitzplusplus::dodirect::Mul()));
    blitz::Array<bool,3> b(3,3,3);
    BOOST_CHECK(blitz::all(res1.dual()==res2));
}

BOOST_AUTO_TEST_CASE(vectorspace_operations)
{
    Array1 x(3), y(3);
    x=1,2,3;
    y=2,3,1;
    I1 i1;
    NOSV_I1 sv1(x, i1, byReference);
    NOSV_I1 sv2(y, i1, byReference);
    NOSV_I1 result_p(sv1 + sv2);
    BOOST_CHECK_EQUAL(result_p()(0), x(0)+y(0));
    BOOST_CHECK_EQUAL(result_p()(1), x(1)+y(1));
    BOOST_CHECK_EQUAL(result_p()(2), x(2)+y(2));

    NOSV_I1 result_m(sv1 - sv2);
    BOOST_CHECK_EQUAL(result_m()(0), x(0)-y(0));
    BOOST_CHECK_EQUAL(result_m()(1), x(1)-y(1));
    BOOST_CHECK_EQUAL(result_m()(2), x(2)-y(2));

    NOSV_I1 result_mul(sv1*dcomp(4));
    BOOST_CHECK_EQUAL(result_mul()(0), x(0)*dcomp(4));
    BOOST_CHECK_EQUAL(result_mul()(1), x(1)*dcomp(4));
    BOOST_CHECK_EQUAL(result_mul()(2), x(2)*dcomp(4));
}

BOOST_AUTO_TEST_CASE(tensorproduct)
{
    I1 i1;
    I2 i2;
    Array1 x(3);
    x = 1,2,3;
    Array2 y(3,3);
    y = 4,3,2,
        5,4,3,
        6,1,2;

    NOSV_I1 sv1(x, i1, byReference);
    NOSV_I2 sv2(y, i2, byReference);

    typedef TensorType<NOSV_I1,NOSV_I2>::type NOSV_I1I2;
    NOSV_I1I2 sv_i1i2(sv1*sv2);
    typedef TensorType<NOSV_I2,NOSV_I1>::type NOSV_I2I1;
    NOSV_I2I1 sv_i2i1(sv2*sv1);
    typedef TensorType<NOSV_I1I2,NOSV_I1>::type NOSV_I1I2I1_a;
    typedef TensorType<NOSV_I1,NOSV_I2I1>::type NOSV_I1I2I1_b;
    BOOST_MPL_ASSERT(( boost::is_same<NOSV_I1I2I1_a, NOSV_I1I2I1_b> ));
    typedef NOSV_I1I2I1_a NOSV_I1I2I1;
    NOSV_I1I2I1(sv_i1i2*sv1);
    NOSV_I1I2I1(sv1*sv_i2i1);

    typedef StateVector<1> OSV;
    OSV osv(x, ByReference());
    NOSV_I2I1 mixed_a(sv2*osv);
    NOSV_I1I2 mixed_b(osv*sv2);
    typedef TensorType<NOSV_I2, OSV>::type NOSV_I2I1_orth;
    typedef TensorType<OSV, NOSV_I2>::type NOSV_I1I2_orth;
}


#include <algorithm>
#include <fstream>
#include <functional>
#include <iostream>
#include <iterator>
#include <numeric>
#include <set>
#include <string>
#include <unordered_map>
#include <vector>

#include <boost/range/counting_range.hpp>

using Location = std::string;
using LocationID = unsigned;
using Distance = unsigned;

struct Route {
	LocationID id1, id2;
	Distance distance;
};

struct RouteEntry {
	Location location_A, location_B;
	Distance distance;
};

auto& operator>>(std::istream& in, RouteEntry& entry) {
	return in >> entry.location_A >> entry.location_B >> entry.distance;
}

template<typename T>
auto factorial(T n) {
	const auto range = boost::counting_range<T>(2, (n + 1));
	return std::accumulate(range.begin(), range.end(), T{1}, std::multiplies{});
}

template<typename T>
auto triangle(T n) {
	return n * (n - 1) / 2;
}

template<typename T>
auto triangle_index(T a, T b) {
	const auto& [min, max] = std::minmax(a, b);
	return min + triangle(max);
}

int main() {

	const auto filename = std::string{"locations.txt"};
	auto file = std::fstream{filename};

	if(file.is_open()) {

		auto locations = std::set<LocationID>{};

		const auto add_location = [&locations] (const auto& location) {

			static auto location_id = LocationID{};
			static auto geo_map = std::unordered_map<Location, LocationID>{};

			const auto pos = geo_map.try_emplace(location, 0);

			auto& current_id = pos.first->second;

			const auto is_new_location = pos.second;

			if(is_new_location) {
				// the ids start from 0, hence the postfix increment
				current_id = location_id++;
				locations.insert(current_id);
			}

			return current_id;
		};

		auto routes = std::vector<Route>{};

		RouteEntry entry;

		while(file >> entry) {
			routes.push_back({
				add_location(entry.location_A),
				add_location(entry.location_B),
				entry.distance
			});
		}

		// ========================================
		// find shortest path through all locations
		// ========================================

		// will be used for various calculations
		const auto size = locations.size();

		// 1) creates a lookup table for distances of all routes
		// 2) doing triangle instead of (size*size) halves the memory footprint,
		// but at the expense of doing additional calculation for each access
		auto chart = std::vector<Distance>(triangle(size));

		// log the distance for any given route between two locations
		for(const auto& route : routes) {
			const auto& index = triangle_index(route.id1, route.id2);
			chart[index] = route.distance;
		}

		auto itinerary = std::vector<LocationID>{locations.begin(), locations.end()};
		auto min_distance = Distance{std::numeric_limits<Distance>::max()};

		// only a subset of all possible permutations of routes is relevant for us,
		// so we create an upper limit to count down from when looping through permutations
		auto limit = (factorial(size) / size) * (size - 1);

		do {
			const auto begin1 = itinerary.begin();
			const auto end1	  = std::prev(itinerary.end());
			const auto begin2 = std::next(begin1);
			const auto acc	  = Distance{};
			const auto op1	  = std::plus{};
			const auto op2	  = [&chart] (auto a, auto b) { return chart[triangle_index(a, b)]; };

			// inner_product resolves to acc = op1(acc, op2(begin1, begin2)) in a loop
			const auto tmp_distance = std::inner_product(begin1, end1, begin2, acc, op1, op2);

			min_distance = std::min(min_distance, tmp_distance);

		} while(((limit--) > 0) && std::next_permutation(itinerary.begin(), itinerary.end()));

		std::cout << min_distance << std::endl;

	} else {
		std::cerr << "Error! Could not open \"" << filename << "\"!" << std::endl;
	}

	return 0;
}