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

// Copyright (c) 2011 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright Akira Takahashi 2011

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

#include <geometry_test_common.hpp>

#include <boost/fusion/include/adapt_struct_named.hpp>

#include <boost/geometry/geometry.hpp>
#include <boost/geometry/geometries/adapted/boost_fusion.hpp>
#include <boost/geometry/geometries/adapted/c_array.hpp>
#include <boost/geometry/geometries/adapted/boost_tuple.hpp>
#include <iostream>

BOOST_GEOMETRY_REGISTER_C_ARRAY_CS(cs::cartesian)
BOOST_GEOMETRY_REGISTER_BOOST_FUSION_CS(cs::cartesian)
BOOST_GEOMETRY_REGISTER_BOOST_TUPLE_CS(cs::cartesian)

struct for_fusion_2d
{
    float x,y;
};
struct for_fusion_3d
{
    double x,y,z;
};

BOOST_FUSION_ADAPT_STRUCT(for_fusion_2d, (float, x) (float, y))
BOOST_FUSION_ADAPT_STRUCT(for_fusion_3d, (double, x) (double, y) (double, z))

void test_2d()
{
    bg::model::point<double, 2, bg::cs::cartesian> p1(1, 2);
    double p2[2] = {3, 4};
    boost::tuple<double, double> p3(5,6);

    for_fusion_2d pf = {7, 8};

    BOOST_CHECK_CLOSE(bg::distance(p1, pf), 8.4852813742385695, 0.01);
    BOOST_CHECK_CLOSE(bg::distance(p2, pf), 5.6568542494923806, 0.01);
    BOOST_CHECK_CLOSE(bg::distance(p3, pf), 2.82843, 0.01);
}

void test_3d()
{
    bg::model::point<double, 3, bg::cs::cartesian> p1(1, 2, 3);
    double p2[3] = {4, 5, 6};
    boost::tuple<double, double, double> p3(7, 8, 9);

    for_fusion_3d pf = {10, 11, 12};

    BOOST_CHECK_CLOSE(bg::distance(p1, pf), 15.58845726811, 0.01);
    BOOST_CHECK_CLOSE(bg::distance(p2, pf), 10.392304845413, 0.01);
    BOOST_CHECK_CLOSE(bg::distance(p3, pf), 5.196152, 0.01);
}

int test_main(int, char* [])
{
    test_2d();
    test_3d();
    return 0;
}


//
// Copyright 2019 Mateusz Loskot <mateusz at loskot dot net>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//
#include <boost/gil.hpp>
#include <boost/gil/extension/numeric/pixel_numeric_operations.hpp>

#include <tuple>
#include <type_traits>

#define BOOST_TEST_MODULE test_ext_numeric_pixel_numeric_operations
#include "unit_test.hpp"
#include "unit_test_utility.hpp"
#include "core/image/test_fixture.hpp" // random_value
#include "core/pixel/test_fixture.hpp"

namespace gil = boost::gil;
namespace fixture = boost::gil::test::fixture;

BOOST_AUTO_TEST_SUITE(pixel_plus_t)

BOOST_AUTO_TEST_CASE_TEMPLATE(plus_integer_same_types, pixel_t, fixture::pixel_integer_types)
{
    using channel_t = typename gil::channel_type<pixel_t>::type;
    gil::pixel_plus_t<pixel_t, pixel_t, pixel_t> f;
    {
        pixel_t p0;
        gil::static_fill(p0, static_cast<channel_t>(0));
        BOOST_TEST(f(p0, p0) == p0);
    }
    {
        pixel_t p1;
        gil::static_fill(p1, static_cast<channel_t>(1));
        pixel_t r2;
        gil::static_fill(r2, static_cast<channel_t>(2));
        BOOST_TEST(f(p1, p1) == r2);
    }
    {
        // Generates pixels with consecutive channel values: {1} or {1,2,3} or {1,2,3,4} etc.
        fixture::consecutive_value<channel_t> g(1);
        pixel_t p;
        gil::static_generate(p, [&g]() { return g(); });
        auto const r = f(p, p);
        BOOST_TEST(r != p);
        BOOST_TEST(gil::at_c<0>(r) == (gil::at_c<0>(p) + gil::at_c<0>(p)));
    }
}

BOOST_AUTO_TEST_SUITE_END() // pixel_plus_t

BOOST_AUTO_TEST_SUITE(pixel_minus_t)

BOOST_AUTO_TEST_CASE_TEMPLATE(minus_integer_same_types, pixel_t, fixture::pixel_integer_types)
{
    using channel_t = typename gil::channel_type<pixel_t>::type;
    gil::pixel_minus_t<pixel_t, pixel_t, pixel_t> f;

    pixel_t p0;
    gil::static_fill(p0, static_cast<channel_t>(0));
    BOOST_TEST(f(p0, p0) == p0);
    {
        pixel_t p1, p2;
        gil::static_fill(p1, static_cast<channel_t>(1));
        gil::static_fill(p2, static_cast<channel_t>(2));
        pixel_t r1;
        gil::static_fill(r1, static_cast<channel_t>(1));
        BOOST_TEST(f(p2, p1) == r1);
    }
    {
        // Generates pixels with consecutive channel values: {1} or {1,2,3} or {1,2,3,4} etc.
        fixture::consecutive_value<channel_t> g(1);
        pixel_t p;
        gil::static_generate(p, [&g]() { return g(); });
        BOOST_TEST(f(p, p) == p0);
    }
}

BOOST_AUTO_TEST_SUITE_END() // pixel_minus_t

BOOST_AUTO_TEST_SUITE(pixel_multiplies_scalar_t)

BOOST_AUTO_TEST_CASE_TEMPLATE(pixel_multiplies_scalar_integer_same_types, pixel_t, fixture::pixel_integer_types)
{
    using channel_t = typename gil::channel_type<pixel_t>::type;
    gil::pixel_multiplies_scalar_t<pixel_t, channel_t, pixel_t> f;

    pixel_t p0;
    gil::static_fill(p0, static_cast<channel_t>(0));
    BOOST_TEST(f(p0, 0) == p0);

    {
        pixel_t p1;
        gil::static_fill(p1, static_cast<channel_t>(1));
        BOOST_TEST(f(p1, 0) == p0);
        BOOST_TEST(f(p1, 1) == p1);
    }
    {
        // Generates pixels with consecutive channel values: {1} or {1,2,3} or {1,2,3,4} etc.
        fixture::consecutive_value<channel_t> g(1);
        pixel_t p;
        gil::static_generate(p, [&g]() { return g(); });

        // check first channel value is doubled
        auto const r = f(p, 2);
        BOOST_TEST(r != p);
        BOOST_TEST(gil::at_c<0>(r) == (gil::at_c<0>(p) * 2));
    }
}

BOOST_AUTO_TEST_SUITE_END() // pixel_multiplies_scalar_t

BOOST_AUTO_TEST_SUITE(pixel_multiply_t)

BOOST_AUTO_TEST_CASE_TEMPLATE(pixel_multiply_integer_same_types, pixel_t, fixture::pixel_integer_types)
{
    using channel_t = typename gil::channel_type<pixel_t>::type;
    gil::pixel_multiply_t<pixel_t, pixel_t, pixel_t> f;

    pixel_t p0;
    gil::static_fill(p0, static_cast<channel_t>(0));
    BOOST_TEST(f(p0, p0) == p0);

    pixel_t p1;
    gil::static_fill(p1, static_cast<channel_t>(1));
    BOOST_TEST(f(p1, p1) == p1);

    pixel_t p2;
    gil::static_fill(p2, static_cast<channel_t>(2));
    BOOST_TEST(f(p1, p2) == p2);
}

BOOST_AUTO_TEST_SUITE_END() // pixel_multiply_t

BOOST_AUTO_TEST_SUITE(pixel_divides_scalar_t)

BOOST_AUTO_TEST_CASE_TEMPLATE(pixel_divides_scalar_integer_same_types, pixel_t, fixture::pixel_integer_types)
{
    using channel_t = typename gil::channel_type<pixel_t>::type;
    gil::pixel_divides_scalar_t<pixel_t, channel_t, pixel_t> f;

    pixel_t p0;
    gil::static_fill(p0, static_cast<channel_t>(0));
    BOOST_TEST(f(p0, 1) == p0);

    pixel_t p1;
    gil::static_fill(p1, static_cast<channel_t>(1));
    BOOST_TEST(f(p1, 1) == p1);

    pixel_t p2;
    gil::static_fill(p2, static_cast<channel_t>(2));
    BOOST_TEST(f(p2, 2) == p1);
}

BOOST_AUTO_TEST_SUITE_END() // pixel_divides_scalar_t

BOOST_AUTO_TEST_SUITE(pixel_divide_t)

BOOST_AUTO_TEST_CASE_TEMPLATE(pixel_divide_integer_same_types, pixel_t, fixture::pixel_integer_types)
{
    using channel_t = typename gil::channel_type<pixel_t>::type;
    gil::pixel_divide_t<pixel_t, pixel_t, pixel_t> f;

    pixel_t p0;
    gil::static_fill(p0, static_cast<channel_t>(0));
    pixel_t p1;
    gil::static_fill(p1, static_cast<channel_t>(1));
    BOOST_TEST(f(p0, p1) == p0);
    BOOST_TEST(f(p1, p1) == p1);

    pixel_t p2;
    gil::static_fill(p2, static_cast<channel_t>(2));
    BOOST_TEST(f(p2, p1) == p2);
}

BOOST_AUTO_TEST_SUITE_END() // pixel_divide_t

BOOST_AUTO_TEST_SUITE(pixel_halves_t)

BOOST_AUTO_TEST_CASE_TEMPLATE(pixel_halves_integer_same_types, pixel_t, fixture::pixel_integer_types)
{
    using channel_t = typename gil::channel_type<pixel_t>::type;
    gil::pixel_halves_t<pixel_t> f;

    pixel_t p0;
    gil::static_fill(p0, static_cast<channel_t>(0));
    pixel_t p1;
    gil::static_fill(p1, static_cast<channel_t>(1));

    {
        auto p = p0;
        BOOST_TEST(f(p) == p0);
    }
    {
        auto p = p1;
        BOOST_TEST(f(p) == p0); // truncates toward Zero
    }
    {
        pixel_t p2;
        gil::static_fill(p2, static_cast<channel_t>(2));
        BOOST_TEST(f(p2) == p1);
    }
}

BOOST_AUTO_TEST_SUITE_END() // pixel_halves_t

BOOST_AUTO_TEST_SUITE(pixel_zeros_t)

BOOST_AUTO_TEST_CASE_TEMPLATE(pixel_zeros_integer_same_types, pixel_t, fixture::pixel_integer_types)
{
    using channel_t = typename gil::channel_type<pixel_t>::type;
    gil::pixel_zeros_t<pixel_t> f;

    pixel_t p0;
    gil::static_fill(p0, static_cast<channel_t>(0));
    {
        auto p = p0;
        BOOST_TEST(f(p) == p0);
    }
    {
        fixture::consecutive_value<channel_t> g(1);
        pixel_t p;
        gil::static_generate(p, [&g]() { return g(); });
        BOOST_TEST(f(p) == p0);
    }
}

BOOST_AUTO_TEST_SUITE_END() // pixel_zeros_t

BOOST_AUTO_TEST_SUITE(zero_channels)

BOOST_AUTO_TEST_CASE_TEMPLATE(zero_channels_integer_same_types, pixel_t, fixture::pixel_integer_types)
{
    using channel_t = typename gil::channel_type<pixel_t>::type;

    pixel_t p0;
    gil::static_fill(p0, static_cast<channel_t>(0));
    {
        auto p = p0;
        gil::zero_channels(p);
        BOOST_TEST(p == p0);
    }
    {
        fixture::consecutive_value<channel_t> g(1);
        pixel_t p;
        gil::static_generate(p, [&g]() { return g(); });
        gil::zero_channels(p);
        BOOST_TEST(p == p0);
    }
}

BOOST_AUTO_TEST_SUITE_END() // zero_channels

BOOST_AUTO_TEST_SUITE(pixel_assigns_t)

BOOST_AUTO_TEST_CASE_TEMPLATE(pixel_assigns_integer_same_types, pixel_t, fixture::pixel_integer_types)
{
    using channel_t = typename gil::channel_type<pixel_t>::type;
    gil::pixel_assigns_t<pixel_t, pixel_t> f;

    {
        pixel_t p0, r;
        gil::static_fill(p0, static_cast<channel_t>(0));
        f(p0, r);
        BOOST_TEST(p0 == r);
    }
    {
        fixture::consecutive_value<channel_t> g(1);
        pixel_t p, r;
        gil::static_generate(p, [&g]() { return g(); });
        f(p, r);
        BOOST_TEST(p == r);
    }
}

BOOST_AUTO_TEST_SUITE_END() // pixel_assigns_t


/*!
 * @file demo.cpp
 * @author  (Aleksander Rybin)
 * @brief Threadpool example demo program for parallel matrix x vector multiply
 * @date 2021-09-15
 */

// ---------------------
// includes: STL
#include <algorithm>
#include <functional>
#include <iomanip>
#include <iostream>
#include <random>
#include <vector>

// ---------------------
// includes: boost
#include <boost/program_options.hpp>

// ---------------------
// includes: local
#include "threadpool_example/threadpool.hpp"

std::vector<int> generate(std::size_t size, std::function<int()> gen);

void printMatrix(const std::vector<int>& matrix, std::size_t matrixRows, std::size_t matrixCols);
void printVector(const std::vector<int>& vector);

int main(int argc, char* argv[]) {
    namespace po = boost::program_options;

    po::options_description options("Allowed programm options");

    std::size_t threadsNum;
    bool        verboseOutput;

    std::size_t matrixRows;
    std::size_t matrixCols;

    // clang-format off
    options.add_options()
        ("help,h", "produce help message")
        ("threads_num,t", po::value<std::size_t>(&threadsNum)->default_value(std::thread::hardware_concurrency() - 1), "number of threads to run")
        ("verbose,v", po::value<bool>(&verboseOutput)->default_value(false), "verbose output")
        ("matrix_rows", po::value<std::size_t>(&matrixRows)->default_value(10000), "number of rows in multiplied matrix")
        ("matrix_cols", po::value<std::size_t>(&matrixCols)->default_value(10000), "number of cols in multiplied matrix");
    // clang-format on

    po::variables_map vm;

    try {
        po::store(po::parse_command_line(argc, argv, options), vm);
        po::notify(vm);
    } catch (const po::error& e) {
        std::cerr << e.what() << '\n';
        return EXIT_FAILURE;
    }

    if (vm.count("help")) {
        std::cout << options << std::endl;
        return EXIT_SUCCESS;
    }

    try {
        std::random_device                 randomDevice;
        std::mt19937                       mersenneEngine{randomDevice()};
        std::uniform_int_distribution<int> distribution{-9, 9};
        auto gen = [&distribution, &mersenneEngine] { return distribution(mersenneEngine); };

        std::vector<int> matrix = generate(matrixRows * matrixCols, gen);
        std::vector<int> vector = generate(matrixCols, gen);

        std::vector<int> result(matrixRows);

        if (verboseOutput) {
            printMatrix(matrix, matrixRows, matrixCols);
            printVector(vector);
        }

        try {
            threadpool_example::ThreadPool thp(threadsNum);

            std::size_t rowsPerThread = matrixRows / threadsNum;
            for (std::size_t i = 0; i < threadsNum; ++i) {
                thp.enqueue([=, &matrix, &vector, &result] {
                    for (std::size_t j = i * rowsPerThread; j < (i + 1) * rowsPerThread; ++j) {
                        int currentResult = 0;
                        for (std::size_t k = 0; k < matrixCols; ++k) {
                            currentResult += matrix[j * matrixCols + k] * vector[k];
                        }
                        result[j] = currentResult;
                    }
                });
            }
        } catch (const std::system_error& e) {
            std::cerr << "thread error" << std::endl;
            return EXIT_FAILURE;
        }

        if (verboseOutput) {
            printVector(result);
        }
    } catch (const std::bad_alloc& e) {
        std::cerr << "bad alock" << std::endl;
        return EXIT_FAILURE;
    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
        return EXIT_FAILURE;
    } catch (...) {
        std::cerr << "unknown error" << std::endl;
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

std::vector<int> generate(std::size_t size, std::function<int()> gen) {
    std::vector<int> result(size);
    std::generate(result.begin(), result.end(), gen);
    return result;
}

void printMatrix(const std::vector<int>& matrix, std::size_t matrixRows, std::size_t matrixCols) {
    std::cout << "\n";
    for (std::size_t i = 0; i < matrixRows; ++i) {
        for (std::size_t j = 0; j < matrixCols; ++j) {
            std::cout << std::setw(2) << matrix[i * matrixCols + j] << ' ';
        }
        std::cout << "\n";
    }
    std::cout << std::endl;
}

void printVector(const std::vector<int>& vector) {
    std::cout << "\n";
    for (std::size_t i = 0; i < vector.size(); ++i) {
        std::cout << std::setw(2) << vector[i] << ' ';
    }
    std::cout << std::endl;
}


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

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

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

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

using namespace depspawn;
using namespace blitz;

Array<int , 1> r(10), a(100);

void addreduce(int& result, const Array<int , 1>& values)
{
  result = 0;

  for(int i = 0; i < values.size(); i++)
    result += values(i);

  LOG("Added " << values.size() << " values -> " << result << " into address " << ((long int)&result));
}

void print(const Array<int , 1>& v, const char * msg = 0)
{
  if(msg)
    std::cout << msg << " : ";
  std::cout << "[ ";
  for(int i = 0; i < 10; i++)
    std::cout << v(i) << ' ' /**/ << ((long int)&(v(i))) << ' ' /**/;
  std::cout << "]\n";
}

int main()
{ int i, result;

  for (i = 0; i < 100; i++) {
    a(i) = i;
  }

  for(i=0; i<100; i+=10) {
    LOG("iter " << i);
    spawn(addreduce, r(i/10), a(Range(i, i+9)));
  }

  spawn(addreduce, result, r);

  wait_for_all();

  print(a,"First ten values of array a to add");
  print(r, "Ten partial reductions of ten consecutive elements");

  std::cout << "The addition of the values 0 to 99 is " << result << " and should be " << 99 * 50 << std::endl;

  return 0;
}


#define BOOST_TEST_MODULE VectorArithmetics
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/sum_kahan.hpp>
#include <boost/test/unit_test.hpp>
#include <vexcl/constants.hpp>
#include <vexcl/vector.hpp>
#include <vexcl/reductor.hpp>
#include <vexcl/element_index.hpp>
#include <vexcl/tagged_terminal.hpp>
#include <vexcl/function.hpp>
#ifndef VEXCL_BACKEND_CUDA
#include <vexcl/vector_view.hpp>
#include <vexcl/constant_address_space.hpp>
#endif
#include <boost/math/constants/constants.hpp>
#include "context_setup.hpp"

BOOST_AUTO_TEST_CASE(access_element)
{
    vex::vector<double> x(ctx, 5);
    x = 5;
    x[2] = 2;
    x.at(3) = 3;

    BOOST_CHECK_EQUAL(x[1], 5.0);
    BOOST_CHECK_EQUAL(x.at(2), 2.0);
    BOOST_CHECK_EQUAL(x[3], 3.0);
    BOOST_CHECK_THROW(x.at(5), std::out_of_range);
}

BOOST_AUTO_TEST_CASE(assign_expression)
{
    const size_t N = 1024;

    vex::vector<double> x(ctx, N);
    vex::vector<double> y(ctx, N);
    vex::vector<double> z(ctx, N);

    y = 42;
    z = 67;
    x = 5 * sin(y) + z;

    check_sample(x, [](size_t, double a) {
            BOOST_CHECK_CLOSE(a, 5 * sin(42.0) + 67, 1e-12);
            });
}

BOOST_AUTO_TEST_CASE(compound_assignment)
{
    const size_t n = 1024;

    vex::vector<double> x(ctx, n);

    x = 0;
    x += 1;

    check_sample(x, [](size_t, double a) { BOOST_CHECK(a == 1); });

    x -= 2;

    check_sample(x, [](size_t, double a) { BOOST_CHECK(a == -1); });
}

BOOST_AUTO_TEST_CASE(reduce_expression)
{
    namespace acc = boost::accumulators;

    const size_t N = 1024;

    std::vector<double> x = random_vector<double>(N);
    std::transform(x.begin(), x.end(), x.begin(), [](double v){ return (v - 0.5) * 1e8; });

    vex::vector<double> X(ctx, x);

    vex::Reductor<double,vex::SUM      > sum(ctx);
    vex::Reductor<double,vex::MIN      > min(ctx);
    vex::Reductor<double,vex::MAX      > max(ctx);
    vex::Reductor<double,vex::SUM_Kahan> csum(ctx);

    acc::accumulator_set< double, acc::stats< acc::tag::sum_kahan > > stat;
    std::for_each(x.begin(), x.end(), std::ref(stat));

    BOOST_CHECK_CLOSE(sum(X),  acc::sum_kahan(stat), 1e-8);
    BOOST_CHECK_CLOSE(csum(X), acc::sum_kahan(stat), 1e-8);

    BOOST_CHECK_EQUAL(min(X), *std::min_element(x.begin(), x.end()));
    BOOST_CHECK_EQUAL(max(X), *std::max_element(x.begin(), x.end()));

#ifndef BOOST_NO_VARIADIC_TEMPLATES
    vex::Reductor<double,vex::MIN_MAX  > minmax(ctx);
    auto mm = minmax(X);
    BOOST_CHECK_EQUAL(mm.s[0], *std::min_element(x.begin(), x.end()));
    BOOST_CHECK_EQUAL(mm.s[1], *std::max_element(x.begin(), x.end()));
#endif

    BOOST_CHECK_EQUAL( max( fabs(X - X) ), 0.0);
}

BOOST_AUTO_TEST_CASE(builtin_functions)
{
    const size_t N = 1024;
    std::vector<double> x = random_vector<double>(N);
    vex::vector<double> X(ctx, x);
    vex::vector<double> Y(ctx, N);

    Y = pow(sin(X), 2.0) + pow(cos(X), 2.0);

    check_sample(Y, [](size_t, double a) { BOOST_CHECK_CLOSE(a, 1, 1e-8); });
}

BOOST_AUTO_TEST_CASE(user_defined_functions)
{
    const size_t N = 1024;

    vex::vector<double> x(ctx, N);
    vex::vector<double> y(ctx, N);

    x = 1;
    y = 2;

    VEX_FUNCTION(size_t, greater, (double, x)(double, y), return x > y;);

    vex::Reductor<size_t,vex::SUM> sum(ctx);

    BOOST_CHECK( sum( greater(x, y) ) == 0U);
    BOOST_CHECK( sum( greater(y, x) ) == N);
}

BOOST_AUTO_TEST_CASE(user_defined_functions_same_signature)
{
    const size_t N = 1024;
    vex::vector<double> x(ctx, N);

    x = 1;

    VEX_FUNCTION(double, times2, (double, x), return x * 2;);
    VEX_FUNCTION(double, times4, (double, x), return x * 4;);

    vex::Reductor<size_t,vex::SUM> sum(ctx);

    BOOST_CHECK( sum( times2(x) ) == 2 * N );
    BOOST_CHECK( sum( times4(x) ) == 4 * N );
}

BOOST_AUTO_TEST_CASE(element_index)
{
    const size_t N = 1024;

    vex::vector<double> x(ctx, N);

    x = sin(0.5 * vex::element_index());

    check_sample(x, [](size_t idx, double a) { BOOST_CHECK_CLOSE(a, sin(0.5 * idx), 1e-6); });
}

#if !defined(VEXCL_BACKEND_CUDA) && !defined(VEXCL_BACKEND_JIT) && !defined(__APPLE__)
BOOST_AUTO_TEST_CASE(vector_values)
{
    const size_t N = 1024;

    VEX_FUNCTION(cl_int4, make_int4, (int, x), return (int4)(x, x, x, x););

    cl_int4 c = {{1, 2, 3, 4}};

    vex::vector<cl_int4> X(ctx, N);
    X = c * (make_int4(5 + vex::element_index()));

    check_sample(X, [c](size_t idx, cl_int4 v) {
            for(int j = 0; j < 4; ++j)
                BOOST_CHECK(v.s[j] == c.s[j] * (5 + static_cast<int>(idx)));
            });
}
#endif

BOOST_AUTO_TEST_CASE(nested_functions)
{
    const size_t N = 1024;

    VEX_FUNCTION(int, f, (int, x), return 2 * x;);
    VEX_FUNCTION(int, g, (int, x), return 3 * x;);

    vex::vector<int> x(ctx, N);

    x = 1;
    x = f(f(x));
    check_sample(x, [](size_t, int a) { BOOST_CHECK(a == 4); });

    x = 1;
    x = g(f(x));
    check_sample(x, [](size_t, int a) { BOOST_CHECK(a == 6); });
}

BOOST_AUTO_TEST_CASE(custom_header)
{
    const size_t n = 1024;

    vex::vector<int> x(ctx, n);

    vex::push_program_header(ctx, "#define THE_ANSWER 42\n");

    VEX_FUNCTION(int, answer, (int, x), return x * THE_ANSWER;);

    x = answer(1);

    check_sample(x, [](size_t, int a) {
            BOOST_CHECK(a == 42);
            });

    vex::pop_program_header(ctx);
}

// Have to define these outside of the following test case scope.
// In Visual C++ is not types defined in enclosing function scope
// are not able to refeence each other.
VEX_FUNCTION(double, sin2, (double, x), return pow(sin(x), 2.0););
VEX_FUNCTION(double, cos2, (double, x), return pow(cos(x), 2.0););

BOOST_AUTO_TEST_CASE(function_with_preamble)
{
    const size_t n = 1024;

    vex::vector<double> x(ctx, random_vector<double>(n));
    vex::vector<double> y(ctx, n);

    VEX_FUNCTION_D(double, one, (double, x), (sin2)(cos2),
            return sin2(x) + cos2(x);
            );

    y = one(x);

    check_sample(y, [](size_t, double a) {
            BOOST_CHECK_CLOSE(a, 1.0, 1e-8);
            });
}

BOOST_AUTO_TEST_CASE(ternary_operator)
{
    const size_t n = 1024;

    vex::vector<double> x(ctx, random_vector<double>(n));
    vex::vector<double> y(ctx, n);

    y = if_else(x > 0.5, sin(x), cos(x));

    check_sample(x, y, [&](size_t, double X, double Y) {
            BOOST_CHECK_CLOSE(Y, X > 0.5 ? sin(X) : cos(X), 1e-8);
            });
}

BOOST_AUTO_TEST_CASE(assign_to_ternary_operator)
{
    const size_t n = 32;

    vex::vector<double> x(ctx, random_vector<double>(n));
    vex::vector<double> y(ctx, n);
    vex::vector<double> z(ctx, n);

    y = 0;
    z = 0;

    vex::tie( *if_else(x < 0.5, &y, &z) ) = 42;

    check_sample(x, y, z, [&](size_t, double X, double Y, double Z) {
            BOOST_CHECK_EQUAL(X < 0.5 ? Y : Z, 42);
            BOOST_CHECK_EQUAL(X < 0.5 ? Z : Y, 0);
            });
}

BOOST_AUTO_TEST_CASE(combine_expressions)
{
    const size_t n = 1024;

    vex::vector<double> x(ctx, n);

    auto alpha  = vex::constants::two_pi() * vex::tag<1>(vex::element_index());
    auto sine   = sin(alpha);
    auto cosine = cos(alpha);

    x = pow(sine, 2.0) + pow(cosine, 2.0);

    check_sample(x, [](size_t, double v) { BOOST_CHECK_CLOSE(v, 1.0, 1e-8); });
}

BOOST_AUTO_TEST_CASE(constants)
{
    const size_t n = 1024;

    vex::vector<double> x(ctx, n);

    x = std::integral_constant<int, 42>();
    check_sample(x, [](size_t, double v) { BOOST_CHECK_EQUAL(v, 42); });

    x = vex::constants::pi();
    check_sample(x, [](size_t, double v) { BOOST_CHECK_CLOSE(v, boost::math::constants::pi<double>(), 1e-8); });
}

#ifndef VEXCL_BACKEND_CUDA
BOOST_AUTO_TEST_CASE(constant_address_space)
{
    std::vector<vex::command_queue> queue(1, ctx.queue(0));

    const size_t N = 1024;

    int host_data[] = {1, 2, 3, 4};

    vex::vector<int> x(queue, 4, host_data, vex::backend::MEM_READ_ONLY);
    vex::vector<int> y(queue, N);

    y = vex::permutation(vex::element_index() % 4)( vex::constant(x) );

    check_sample(y, [&](size_t idx, int v) {
            BOOST_CHECK_EQUAL(v, host_data[idx % 4]);
            });
}
#endif

#if (VEXCL_CHECK_SIZES > 0)
BOOST_AUTO_TEST_CASE(expression_size_check)
{
    vex::vector<int> x(ctx, 16);
    vex::vector<int> y(ctx, 32);

    BOOST_CHECK_THROW(x = y, std::runtime_error);
}
#endif

BOOST_AUTO_TEST_SUITE_END()


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

// @sect3{Include files}

// 像往常一样，在开始的时候，我们把所有我们需要的头文件都包含在这里。除了为Trilinos库提供接口的各种文件外，没有什么意外。

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

#include <deal.II/lac/affine_constraints.h>
#include <deal.II/lac/vector.h>
#include <deal.II/lac/full_matrix.h>
#include <deal.II/lac/dynamic_sparsity_pattern.h>
#include <deal.II/lac/solver_cg.h>
#include <deal.II/lac/trilinos_sparse_matrix.h>
#include <deal.II/lac/trilinos_vector.h>
#include <deal.II/lac/trilinos_precondition.h>

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

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

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

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

#include <fstream>
#include <iostream>

namespace Step41
{
  using namespace dealii;
// @sect3{The ObstacleProblem class template}

// 该类提供了描述障碍问题所需的所有函数和变量。它与我们在 step-4 中要做的事情很接近，所以相对简单。唯一真正的新组件是计算主动集合的update_solution_and_constraints函数和一些描述线性系统原始（无约束）形式所需的变量（ complete_system_matrix 和 complete_system_rhs ），以及主动集合本身和主动集合公式中用于缩放拉格朗日乘数的质量矩阵 $B$ 的对角线。其余的内容与 step-4 相同。

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

  private:
    void make_grid();
    void setup_system();
    void assemble_system();
    void
         assemble_mass_matrix_diagonal(TrilinosWrappers::SparseMatrix &mass_matrix);
    void update_solution_and_constraints();
    void solve();
    void output_results(const unsigned int iteration) const;

    Triangulation<dim>        triangulation;
    FE_Q<dim>                 fe;
    DoFHandler<dim>           dof_handler;
    AffineConstraints<double> constraints;
    IndexSet                  active_set;

    TrilinosWrappers::SparseMatrix system_matrix;
    TrilinosWrappers::SparseMatrix complete_system_matrix;

    TrilinosWrappers::MPI::Vector solution;
    TrilinosWrappers::MPI::Vector system_rhs;
    TrilinosWrappers::MPI::Vector complete_system_rhs;
    TrilinosWrappers::MPI::Vector diagonal_of_mass_matrix;
    TrilinosWrappers::MPI::Vector contact_force;
  };
// @sect3{Right hand side, boundary values, and the obstacle}

// 在下文中，我们定义了描述右侧函数、Dirichlet边界值以及作为 $\mathbf x$ 函数的障碍物高度的类。在这三种情况下，我们都从函数 @<dim@>, 派生出这些类，尽管在 RightHandSide 和 Obstacle 的情况下，这更多的是出于惯例而非必要，因为我们从未将此类对象传递给库。在任何情况下，鉴于我们选择了 $f=-10$  ,  $u|_{\partial\Omega}=0$  ...，右手和边界值类的定义是显而易见的。

  template <int dim>
  class RightHandSide : public Function<dim>
  {
  public:
    virtual double value(const Point<dim> & /*p*/,
                         const unsigned int component = 0) const override
    {
      (void)component;
      AssertIndexRange(component, 1);

      return -10;
    }
  };

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

      return 0;
    }
  };

// 我们用一个级联的障碍物来描述障碍物的功能（想想看：楼梯的阶梯）。

  template <int dim>
  class Obstacle : public Function<dim>
  {
  public:
    virtual double value(const Point<dim> & p,
                         const unsigned int component = 0) const override
    {
      (void)component;
      Assert(component == 0, ExcIndexRange(component, 0, 1));

      if (p(0) < -0.5)
        return -0.2;
      else if (p(0) >= -0.5 && p(0) < 0.0)
        return -0.4;
      else if (p(0) >= 0.0 && p(0) < 0.5)
        return -0.6;
      else
        return -0.8;
    }
  };

//  @sect3{Implementation of the ObstacleProblem class}
// @sect4{ObstacleProblem::ObstacleProblem}

// 对每个看过前几个教程程序的人来说，构造函数是完全显而易见的。

  template <int dim>
  ObstacleProblem<dim>::ObstacleProblem()
    : fe(1)
    , dof_handler(triangulation)
  {}
// @sect4{ObstacleProblem::make_grid}

// 我们在二维的正方形 $[-1,1]\times [-1,1]$ 上解决我们的障碍物问题。因此这个函数只是设置了一个最简单的网格。

  template <int dim>
  void ObstacleProblem<dim>::make_grid()
  {
    GridGenerator::hyper_cube(triangulation, -1, 1);
    triangulation.refine_global(7);

    std::cout << "Number of active cells: " << triangulation.n_active_cells()
              << std::endl
              << "Total number of cells: " << triangulation.n_cells()
              << std::endl;
  }
// @sect4{ObstacleProblem::setup_system}

// 在这个值得注意的第一个函数中，我们设置了自由度处理程序，调整了向量和矩阵的大小，并处理了约束。最初，约束条件当然只是由边界值给出的，所以我们在函数的顶部对它们进行插值。

  template <int dim>
  void ObstacleProblem<dim>::setup_system()
  {
    dof_handler.distribute_dofs(fe);
    active_set.set_size(dof_handler.n_dofs());

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

    VectorTools::interpolate_boundary_values(dof_handler,
                                             0,
                                             BoundaryValues<dim>(),
                                             constraints);
    constraints.close();

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

    system_matrix.reinit(dsp);
    complete_system_matrix.reinit(dsp);

    IndexSet solution_index_set = dof_handler.locally_owned_dofs();
    solution.reinit(solution_index_set, MPI_COMM_WORLD);
    system_rhs.reinit(solution_index_set, MPI_COMM_WORLD);
    complete_system_rhs.reinit(solution_index_set, MPI_COMM_WORLD);
    contact_force.reinit(solution_index_set, MPI_COMM_WORLD);

// 这里唯一要做的事情是计算 $B$ 矩阵中的因子，该矩阵用于缩放残差。正如在介绍中所讨论的，我们将使用一个小技巧来使这个质量矩阵成为对角线，在下文中，首先将所有这些计算成一个矩阵，然后提取对角线元素供以后使用。

    TrilinosWrappers::SparseMatrix mass_matrix;
    mass_matrix.reinit(dsp);
    assemble_mass_matrix_diagonal(mass_matrix);
    diagonal_of_mass_matrix.reinit(solution_index_set);
    for (unsigned int j = 0; j < solution.size(); j++)
      diagonal_of_mass_matrix(j) = mass_matrix.diag_element(j);
  }
// @sect4{ObstacleProblem::assemble_system}

// 这个函数一次就把系统矩阵和右手边集合起来，并把约束条件（由于活动集以及来自边界值）应用到我们的系统中。否则，它在功能上等同于例如  step-4  中的相应函数。

  template <int dim>
  void ObstacleProblem<dim>::assemble_system()
  {
    std::cout << "   Assembling system..." << std::endl;

    system_matrix = 0;
    system_rhs    = 0;

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

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

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

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

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

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

        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)
                cell_matrix(i, j) +=
                  (fe_values.shape_grad(i, q_point) *
                   fe_values.shape_grad(j, q_point) * fe_values.JxW(q_point));

              cell_rhs(i) +=
                (fe_values.shape_value(i, q_point) *
                 right_hand_side.value(fe_values.quadrature_point(q_point)) *
                 fe_values.JxW(q_point));
            }

        cell->get_dof_indices(local_dof_indices);

        constraints.distribute_local_to_global(cell_matrix,
                                               cell_rhs,
                                               local_dof_indices,
                                               system_matrix,
                                               system_rhs,
                                               true);
      }
  }

//  @sect4{ObstacleProblem::assemble_mass_matrix_diagonal}

// 下一个函数用于计算对角线质量矩阵 $B$ ，用于在主动集方法中缩放变量。正如介绍中所讨论的，我们通过选择正交的梯形规则来获得质量矩阵的对角线。这样一来，我们就不再需要在正交点、指数 $i$ 和指数 $j$ 上进行三重循环，而是可以直接使用双重循环。考虑到我们在以前的许多教程程序中讨论过的内容，该函数的其余部分是显而易见的。

// 注意在调用这个函数的时候，约束对象只包含边界值约束；因此我们在最后的复制-本地-全局步骤中不必注意保留矩阵项的值，这些项以后可能会受到活动集的约束。

// 还需要注意的是，只有在我们拥有 $Q_1$ 元素的情况下，使用梯形规则的技巧才有效。对于更高阶的元素，我们需要使用一个正交公式，在有限元的所有支持点都有正交点。构建这样一个正交公式其实并不难，但不是这里的重点，所以我们只是在函数的顶部断言我们对有限元的隐含假设实际上得到了满足。

  template <int dim>
  void ObstacleProblem<dim>::assemble_mass_matrix_diagonal(
    TrilinosWrappers::SparseMatrix &mass_matrix)
  {
    Assert(fe.degree == 1, ExcNotImplemented());

    const QTrapezoid<dim> quadrature_formula;
    FEValues<dim>         fe_values(fe,
                            quadrature_formula,
                            update_values | update_JxW_values);

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

    FullMatrix<double> cell_matrix(dofs_per_cell, dofs_per_cell);
    std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);

      {
        fe_values.reinit(cell);
        cell_matrix = 0;

        for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
          for (unsigned int i = 0; i < dofs_per_cell; ++i)
            cell_matrix(i, i) +=
              (fe_values.shape_value(i, q_point) *
               fe_values.shape_value(i, q_point) * fe_values.JxW(q_point));

        cell->get_dof_indices(local_dof_indices);

        constraints.distribute_local_to_global(cell_matrix,
                                               local_dof_indices,
                                               mass_matrix);
      }
  }
// @sect4{ObstacleProblem::update_solution_and_constraints}

// 在某种意义上，这是本程序的核心功能。 它更新了介绍中所讨论的受限自由度的活动集，并从中计算出一个AffineConstraints对象，然后可以用来在下一次迭代的解中消除受限自由度。同时，我们将解决方案的受限自由度设置为正确的值，即障碍物的高度。

// 从根本上说，这个函数是相当简单的。我们必须在所有自由度上循环，并检查函数 $\Lambda^k_i + c([BU^k]_i - G_i) = \Lambda^k_i + cB_i(U^k_i - [g_h]_i)$ 的符号，因为在我们的例子中 $G_i = B_i[g_h]_i$  。为此，我们使用介绍中给出的公式，通过该公式我们可以计算出拉格朗日乘数，作为原始线性系统的残差（通过变量 complete_system_matrix and complete_system_rhs 给出。在这个函数的顶部，我们使用一个属于矩阵类的函数来计算这个残差。

  template <int dim>
  void ObstacleProblem<dim>::update_solution_and_constraints()
  {
    std::cout << "   Updating active set..." << std::endl;

    const double penalty_parameter = 100.0;

    TrilinosWrappers::MPI::Vector lambda(
      complete_index_set(dof_handler.n_dofs()));
    complete_system_matrix.residual(lambda, solution, complete_system_rhs);

// 计算 contact_force[i] =

// - lambda[i] * diagonal_of_mass_matrix[i]。

    contact_force = lambda;
    contact_force.scale(diagonal_of_mass_matrix);
    contact_force *= -1;

// 下一步是重置活动集和约束对象，并在所有自由度上开始循环。由于我们不能只是在解向量的所有元素上循环，所以这变得稍微复杂了一些，因为我们没有办法找出一个自由度与哪个位置相关；但是，我们需要这个位置来测试一个自由度的位移是大于还是小于这个位置的障碍物高度。

// 我们通过在所有单元和定义在每个单元上的DoF上循环来解决这个问题。我们在这里使用一个 $Q_1$ 函数来描述位移，对于该函数，自由度总是位于单元格的顶点上；因此，我们可以通过询问顶点来获得每个自由度的索引及其位置。另一方面，这显然对高阶元素不起作用，因此我们添加了一个断言，确保我们只处理所有自由度都位于顶点的元素，以避免万一有人想玩增加解的多项式程度时用非功能性代码绊倒自己。

// 循环单元格而不是自由度的代价是我们可能会多次遇到一些自由度，即每次我们访问与给定顶点相邻的一个单元格时。因此，我们必须跟踪我们已经接触过的顶点和尚未接触的顶点。我们通过使用一个标志数组来做到这一点  dof_touched  。

    constraints.clear();
    active_set.clear();

    const Obstacle<dim> obstacle;
    std::vector<bool>   dof_touched(dof_handler.n_dofs(), false);

    for (const auto &cell : dof_handler.active_cell_iterators())
      for (const auto v : cell->vertex_indices())
        {
          Assert(dof_handler.get_fe().n_dofs_per_cell() == cell->n_vertices(),
                 ExcNotImplemented());

          const unsigned int dof_index = cell->vertex_dof_index(v, 0);

          if (dof_touched[dof_index] == false)
            dof_touched[dof_index] = true;
          else
            continue;

// 现在我们知道我们还没有触及这个DoF，让我们得到那里的位移函数的值以及障碍函数的值，并使用这个来决定当前DoF是否属于活动集。为此，我们使用上面和介绍中给出的函数。

// 如果我们决定该DoF应该是活动集的一部分，我们将其索引添加到活动集中，在AffineConstraints对象中引入一个不均匀的平等约束，并将解的值重置为障碍物的高度。最后，系统的非接触部分的残差作为一个额外的控制（残差等于剩余的、未计算的力，在接触区之外应该为零），所以我们把残差向量的分量（即拉格朗日乘数lambda）清零，这些分量对应于身体接触的区域；在所有单元的循环结束时，残差将因此只包括非接触区的残差。我们在循环结束后输出这个残差的准则和活动集的大小。

          const double obstacle_value = obstacle.value(cell->vertex(v));
          const double solution_value = solution(dof_index);

          if (lambda(dof_index) + penalty_parameter *
                                    diagonal_of_mass_matrix(dof_index) *
                                    (solution_value - obstacle_value) <
              0)
            {
              active_set.add_index(dof_index);
              constraints.add_line(dof_index);
              constraints.set_inhomogeneity(dof_index, obstacle_value);

              solution(dof_index) = obstacle_value;

              lambda(dof_index) = 0;
            }
        }
    std::cout << "      Size of active set: " << active_set.n_elements()
              << std::endl;

    std::cout << "   Residual of the non-contact part of the system: "
              << lambda.l2_norm() << std::endl;

// 在最后一步中，我们将迄今为止从活动集合中得到的对DoF的约束加入到那些由Dirichlet边界值产生的约束中，并关闭约束对象。

    VectorTools::interpolate_boundary_values(dof_handler,
                                             0,
                                             BoundaryValues<dim>(),
                                             constraints);
    constraints.close();
  }
// @sect4{ObstacleProblem::solve}

// 关于求解函数，其实没有什么可说的。在牛顿方法的背景下，我们通常对非常高的精度不感兴趣（为什么要求一个高度精确的线性问题的解，而我们知道它只能给我们一个非线性问题的近似解），所以我们使用ReductionControl类，当达到一个绝对公差（为此我们选择 $10^{-12}$ ）或者当残差减少一定的系数（这里是 $10^{-3}$ ）时停止反复运算。

  template <int dim>
  void ObstacleProblem<dim>::solve()
  {
    std::cout << "   Solving system..." << std::endl;

    ReductionControl                        reduction_control(100, 1e-12, 1e-3);
    SolverCG<TrilinosWrappers::MPI::Vector> solver(reduction_control);
    TrilinosWrappers::PreconditionAMG       precondition;
    precondition.initialize(system_matrix);

    solver.solve(system_matrix, solution, system_rhs, precondition);
    constraints.distribute(solution);

    std::cout << "      Error: " << reduction_control.initial_value() << " -> "
              << reduction_control.last_value() << " in "
              << reduction_control.last_step() << " CG iterations."
              << std::endl;
  }
// @sect4{ObstacleProblem::output_results}

// 我们使用vtk-format进行输出。 该文件包含位移和活动集的数字表示。

  template <int dim>
  void ObstacleProblem<dim>::output_results(const unsigned int iteration) const
  {
    std::cout << "   Writing graphical output..." << std::endl;

    TrilinosWrappers::MPI::Vector active_set_vector(
      dof_handler.locally_owned_dofs(), MPI_COMM_WORLD);
    for (const auto index : active_set)
      active_set_vector[index] = 1.;

    DataOut<dim> data_out;

    data_out.attach_dof_handler(dof_handler);
    data_out.add_data_vector(solution, "displacement");
    data_out.add_data_vector(active_set_vector, "active_set");
    data_out.add_data_vector(contact_force, "lambda");

    data_out.build_patches();

    std::ofstream output_vtk("output_" +
                             Utilities::int_to_string(iteration, 3) + ".vtk");
    data_out.write_vtk(output_vtk);
  }

//  @sect4{ObstacleProblem::run}

// 这是一个对所有事情都有最高级别控制的函数。 它并不长，而且事实上相当直接：在主动集方法的每一次迭代中，我们都要组装线性系统，求解它，更新主动集并将解投射回可行集，然后输出结果。只要主动集在前一次迭代中没有变化，迭代就会终止。

// 唯一比较棘手的部分是，我们必须在第一次迭代组装好线性系统（即矩阵和右手边）后保存它。原因是这是唯一一个我们可以在没有任何接触约束的情况下访问线性系统的步骤。我们需要这个来计算其他迭代中解决方案的残差，但是在其他迭代中，我们形成的线性系统中对应于约束自由度的行和列都被消除了，因此我们不能再访问原始方程的全部残差。

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

    IndexSet active_set_old(active_set);
    for (unsigned int iteration = 0; iteration <= solution.size(); ++iteration)
      {
        std::cout << "Newton iteration " << iteration << std::endl;

        assemble_system();

        if (iteration == 0)
          {
            complete_system_matrix.copy_from(system_matrix);
            complete_system_rhs = system_rhs;
          }

        solve();
        update_solution_and_constraints();
        output_results(iteration);

        if (active_set == active_set_old)
          break;

        active_set_old = active_set;

        std::cout << std::endl;
      }
  }
} // namespace Step41
// @sect3{The main function}

// 这就是主函数。它遵循所有其他主函数的模式。调用初始化MPI是因为我们在这个程序中建立线性求解器的Trilinos库需要它。

int main(int argc, char *argv[])
{
  try
    {
      using namespace dealii;
      using namespace Step41;

      Utilities::MPI::MPI_InitFinalize mpi_initialization(
        argc, argv, numbers::invalid_unsigned_int);

// 这个程序只能在串行中运行。否则，将抛出一个异常。

      AssertThrow(Utilities::MPI::n_mpi_processes(MPI_COMM_WORLD) == 1,
                  ExcMessage(
                    "This program can only be run in serial, use ./step-41"));

      ObstacleProblem<2> obstacle_problem;
      obstacle_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;
}


#include <Eigen/Dense>
#include <Eigen/LU>
#include <Eigen/SVD>
#include <iostream>
#include <chrono>

#include "align/lmkfit_pipe.h"
#include "align/rigid_pipe.h"
#include "align/bsfit_pipe.h"
#include "align/rigid_pipe.h"
#include "io/ply/meshio.h"

namespace
{
using namespace telef::io;
using namespace telef::align;

Eigen::MatrixXf select_rows(
    const Eigen::MatrixXf &mat,
    const std::vector<int> &inds,
    const int rows,
    const Eigen::Matrix4f transformation)
{
  Eigen::MatrixXf result(rows*inds.size(), mat.cols());
  for (size_t i=0; i<inds.size(); i++)
    {
      Eigen::Matrix3Xf pos = mat.block(3*inds[i],0,3,mat.cols());
      Eigen::Matrix3Xf tpos =
        (transformation * pos.colwise().homogeneous()).colwise().hnormalized();
      result.block(rows*i,0,rows,mat.cols()) = tpos.block(0,0,rows,mat.cols());
    }
  return result;
}

boost::shared_ptr<PCANonRigidAlignmentSuite>
step_position(boost::shared_ptr<PCANonRigidAlignmentSuite> in, float reg)
{
  const auto landmark3d = in->fittingSuite->landmark3d;
  Eigen::VectorXf ref = in->pca_model->getReferenceVector() +
    (in->pca_model->getExpressionBasisMatrix()*in->expressionCoeff);
  const auto lmkInds = in->pca_model->getLandmarks();
  const auto shapeRank = in->pca_model->getShapeRank();
  Eigen::MatrixXf shapeMat = in->pca_model->getShapeBasisMatrix();
  Eigen::MatrixXf shapeLmkMat = select_rows(shapeMat, lmkInds, 3, in->transformation);

  Eigen::MatrixXf regMat =
    reg*Eigen::MatrixXf::Identity(shapeRank, shapeRank);

  // Set up RHS
  Eigen::VectorXf b = Eigen::VectorXf::Zero(3*lmkInds.size());
  for(int i=0; i<landmark3d->size(); i++)
    {
      Eigen::VectorXf m = ref.segment(3*lmkInds[i],3);
      Eigen::VectorXf tm = (in->transformation*m.homogeneous()).hnormalized();
      b(3*i+0) = landmark3d->points[i].x - tm(0);
      b(3*i+1) = landmark3d->points[i].y - tm(1);
      b(3*i+2) = landmark3d->points[i].z - tm(2);
    }

  // Set up LHS
  Eigen::MatrixXf A = shapeLmkMat;

  Eigen::MatrixXf ATA = A.transpose()*A + regMat;
  Eigen::VectorXf ATb = A.transpose()*b;

  Eigen::VectorXf x = ATA.householderQr().solve(ATb);

  Eigen::VectorXf idCoeff = x;

  in->shapeCoeff = idCoeff;

  return in;
}

boost::shared_ptr<PCANonRigidAlignmentSuite>
step_shape(boost::shared_ptr<PCANonRigidAlignmentSuite> in)
{
  PCAGPUNonRigidFittingPipe shfit(10, 2500, 0.10, true, true, false);
  auto res = shfit(in);
  in->shapeCoeff = res->shapeCoeff;

  return in;
}

boost::shared_ptr<PCANonRigidAlignmentSuite>
step_expression(boost::shared_ptr<PCANonRigidAlignmentSuite> in)
{
  BsFitPipe bsfit;
  return bsfit(in);
}

boost::shared_ptr<PCANonRigidAlignmentSuite>
step_pose(boost::shared_ptr<PCANonRigidAlignmentSuite> in)
{
  PCARigidFittingPipe posefit;
  return posefit(in);
}
}

namespace telef::align
{

LmkFitPipe::LmkFitPipe(float reg) :
    m_reg(reg),
    m_prevShape()
{}

boost::shared_ptr<PCANonRigidAlignmentSuite>
LmkFitPipe::_processData(boost::shared_ptr<PCANonRigidAlignmentSuite> in)
{
  auto res = in;
  std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
  if(0 == m_prevShape.size())
    {
      res = step_position(res, m_reg);
      Eigen::VectorXf shapeCoeff = res->shapeCoeff;
      for(int i=0; i<10; i++)
        {
            res = step_shape(res);
            //shapeCoeff = 0.8*shapeCoeff + 0.2*res->shapeCoeff;
            shapeCoeff = res->shapeCoeff;
            res = step_expression(res);
            res = step_pose(res);
        }
        m_prevShape = shapeCoeff;
        res->shapeCoeff = shapeCoeff;

        auto exr = in->pca_model->getExpressionRank();
        for(int i=0; i<exr; i++)
          {
            Eigen::VectorXf expressionCoeff = Eigen::VectorXf::Zero(exr);
            expressionCoeff(i) = 1.0f;
            ColorMesh bs = in->pca_model->genMesh(shapeCoeff, expressionCoeff);
            telef::io::ply::writePlyMesh("bs_"+std::to_string(i)+".ply", bs);
          }
    }
  else
    {
      res->shapeCoeff = m_prevShape;
    }
  std::chrono::duration<double> dur = std::chrono::system_clock::now()-start;
  std::cout << "Time (Lmkfit): " <<
    std::chrono::duration_cast<std::chrono::milliseconds>(dur).count() << "ms" << std::endl;
  return res;
}
}


#include "lum_retinex.h"

#include <Eigen/SparseCore>
#include <Eigen/SparseCholesky>
#include <chrono>

namespace lum {

    // timing helper
	double get_s() {
		using namespace std::chrono;
		auto now = system_clock::now();
		system_clock::duration tse = now.time_since_epoch();
		return duration_cast<nanoseconds>(tse).count() / 1e9;
	}

    // Helper image processing routines
	void log(const float* inimg, float* outimg, int sz) {
#ifdef USE_MKL
		vsLn(sz, inimg, outimg);
#else
		for (int i = 0; i < sz; ++i) {
			float in = inimg[i];
			outimg[i] = std::logf(inimg[i] + 0.00001);
		}
#endif
	}

	void exp(const float* inimg, float* outimg, int sz) {
#ifdef USE_MKL
		vsExp(sz, inimg, outimg);
#else
		for (int i = 0; i < sz; ++i) {
			outimg[i] = std::expf(inimg[i]);
		}
#endif
	}
	void mean(const float* inimg, float* outimg, int outsz) {
		for (int i = 0; i < outsz; ++i) {
			outimg[i] = (inimg[i * 3] + inimg[i * 3 + 1] + inimg[i * 3 + 2]) / 3;
		}
	}

	// helpers for image indices
	class reflshadidx {
	public:
		reflshadidx(int w, int h)
		:m_w(w), m_h(h){}
		int reflidx(int x, int y) const {
			return m_w * y + x;
		}
		int shadidx(int x, int y) const {
			return m_w * m_h + reflidx(x, y);
		}
	private:
		int m_w;
		int m_h;
	};
	class imwrap {
	public:
		imwrap(const float* img, int w, int h)
		:m_w(w), m_h(h), m_img(img){ }

		float operator()(int x, int y) const {
			assert(x >= 0);
			assert(y >= 0);
			assert(x < m_w);
			assert(y < m_h);
			return m_img[m_w * y + x];
		}
	private:
		const float* m_img;
		int m_w;
		int m_h;
	};

	// forward declaration of internal functions
	void reflect_clamp(int w, int h, float* refl_in, float* shading_in);
	void preprocess(int w, int h, const float* img, float* logimg);
	void postprocess(int w, int h, float* refl_in, float* shading_in, float* refl_out, float* shading_out);
	Eigen::VectorXf makeB(float threshold, const float* im, int w, int h);

	using Triplet = Eigen::Triplet<float>;
	int nconstraints(int w, int h) {
		return w*h + 2 * w*(h - 1) + 2 * (w - 1) * h;
	}
	int nentries(int w, int h) {
		return 2 * (w*h + 2 * w*(h - 1) + 2 * (w - 1) * h);
	}
	std::vector<Triplet> makeTriplets(int w, int h) {
		reflshadidx I(w, h);
		printf("Assemble matrix.\n");
		double assemble_start = get_s();
		std::vector <Triplet> entries;
		int cit = 0;
		for (int y = 0; y < h; ++y) {
			for (int x = 0; x < w; ++x) {
				if (x < w - 1) {
					// dxR(r, c) = -lR(r, c) + lR(r, c + 1)
					entries.push_back(Triplet(cit, I.reflidx(x, y), -1));
					entries.push_back(Triplet(cit, I.reflidx(x + 1, y), +1));
					cit++;

					// dxS(r, c) = -lS(r, c) + lS(r, c + 1)
					entries.push_back(Triplet(cit, I.shadidx(x, y), -1));
					entries.push_back(Triplet(cit, I.shadidx(x + 1, y), +1));
					cit++;
				}
				if (y < h - 1) {
					entries.push_back(Triplet(cit, I.reflidx(x, y), -1));
					entries.push_back(Triplet(cit, I.reflidx(x, y + 1), +1));
					cit++;

					entries.push_back(Triplet(cit, I.shadidx(x, y), -1));
					entries.push_back(Triplet(cit, I.shadidx(x, y + 1), +1));
					cit++;
					// dyR(r, c) = -lR(r, c) + lR(r + 1)
					// dyS(r, c) = -lS(r, c) + lS(r + 1)
				}

				// reflectance plus shading (log space) == final image
				entries.push_back(Triplet(cit, I.reflidx(x, y), 1));
				entries.push_back(Triplet(cit, I.shadidx(x, y), 1));
				cit++;
			}
		}
		assert(entries.size() == nentries(w, h));
		double assemble_end = get_s();
		printf("Makemtx took %.1fms\n", (assemble_end- assemble_start) * 1000);
		return entries;
	}

	// to log domain
	void preprocess(int w, int h, const float* img, float* logimg) {
		printf("Start Preprocessing.\n");
		double preprocess_start = get_s();
		log(img, logimg, w*h);
		double preprocess_b_end = get_s();
		printf("Preprocess took %.1fms\n", (preprocess_b_end - preprocess_start) * 1000);
	}

	// back to linear
	void postprocess(int w, int h, float* refl_in, float* shading_in, float* refl_out, float* shading_out) {
		printf("Post process start.\n");
		double postprocess_start = get_s();
		reflect_clamp(w, h, refl_in, shading_in);
		exp(refl_in, refl_out, w*h);
		exp(shading_in, shading_out, w*h);
		double postprocess_end = get_s();
		printf("Postprocess took %.1fms\n", (postprocess_end - postprocess_start)*1000 );
	}

	Eigen::VectorXf makeB(float threshold, const float* im, int w, int h) {
		Eigen::VectorXf b(nconstraints(w, h));
		double assemble_b_start = get_s();
		imwrap I(im, w, h);
		int cit = 0;
		for (int y = 0; y < h; ++y) {
			for (int x = 0; x < w; ++x) {
				if (x < w - 1) {
					float dx = -I(x, y) + I(x + 1, y);
					float dxR;
					float dxS;
					if (std::abs(dx) > threshold) {
						dxR = dx;
						dxS = 0;
					} else {
						dxR = 0;
						dxS = dx;
					}
					// dxR(r, c) = -lR(r, c) + lR(r, c + 1)
					b(cit++) = dxR;

					// dxS(r, c) = -lS(r, c) + lS(r, c + 1)
					b(cit++) = dxS;
				}
				if (y < h - 1) {
					float dy = -I(x, y) + I(x, y + 1);
					float dyR;
					float dyS;
					if (std::abs(dy) > threshold) {
						dyR = dy;
						dyS = 0;
					} else {
						dyR = 0;
						dyS = dy;
					}
					b(cit++) = dyR;
					b(cit++) = dyS;
					// dyR(r, c) = -lR(r, c) + lR(r + 1)
					// dyS(r, c) = -lS(r, c) + lS(r + 1)
				}
				// reflectance plus shading (log space) == final image
				b(cit++) = I(x, y);
			}
		}
		return b;
	}

	// operates on log-reflectance
	// makes sure that log-reflectane is less than 0
	void reflect_clamp(int w, int h, float* refl_in, float* shading_in) {

		float max_reflectance = -FLT_MIN;
		float min_reflectance = FLT_MAX;
		int nancount = 0;
		int infcount = 0;
		for (int i = 0; i < w * h; ++i) {
			if (refl_in[i] > max_reflectance) {
				max_reflectance = refl_in[i];
			}
			if (refl_in[i] < min_reflectance) {
				min_reflectance = refl_in[i];
			}
		}

		if (max_reflectance > 0) {
			for (int i = 0; i < w * h; ++i) {
				refl_in[i] -= max_reflectance;
			}
			for (int i = 0; i < w * h; ++i) {
				shading_in[i] += max_reflectance;
			}
		}
	}

	void retinex(float threshold, const float* im, int w, int h, float* reflectance, float* shading) {
		assert(reflectance);
		assert(shading);
		assert(im);
		retinex_decomp rdec(w, h);
		rdec.solve(threshold, im, reflectance, shading);
	}
	/* I would prefer solving direction Ax = b.
	  However, this doesn't work with Eigen's QR decomp (why?).
	  I solve A'Ax = A'b, using Cholesky, instead.
	*/
	retinex_decomp::retinex_decomp(int w, int h): m_w(w), m_h(h) {
		std::vector <Triplet> entries = makeTriplets(w, h);
		SpMat A(nconstraints(w, h), w * h * 2);
		A.setFromTriplets(entries.begin(), entries.end());
		m_At = A.transpose();
		{
			printf("factorize %d-by-%d matrix\n", A.cols(), A.cols());
			double decompose_start = get_s();
			m_solver.compute(m_At * A);
			double decompose_end = get_s();
			printf("factorize took %.1fms\n", (decompose_end - decompose_start) * 1000);
		}
	}
	void retinex_decomp::solve(float threshold, const float* im, float* reflectance, float* shading) {
		assert(reflectance);
		assert(shading);
		assert(im);

		std::vector<float> logimg(m_w*m_h);
		preprocess(m_w, m_h, im, logimg.data());
		Eigen::VectorXf b = makeB(threshold, logimg.data(), m_w, m_h);
		double solve_start = get_s();
		Eigen::VectorXf x = m_solver.solve(m_At * b);
		double solve_end = get_s();
		printf("solve took %.1fms\n", (solve_end - solve_start) * 1000);
		postprocess(m_w, m_h, x.data(), x.data() + m_w * m_h, reflectance, shading);
	}

	// does greyscale retinex with post processing.
	void retinex_decomp::solve_rgb(float threshold, const float* im, float* reflectance, float* shading) {
		assert(reflectance);
		assert(shading);
		assert(im);
		const int sz = m_w * m_h;

		std::vector<float> grayimg(sz);
		mean(im, grayimg.data(), grayimg.size());
		std::vector<float> graylog(sz);
		double before = get_s();
		log(grayimg.data(), graylog.data(), sz);
		double after = get_s();

		Eigen::VectorXf b = makeB(threshold, graylog.data(), m_w, m_h);
		double solve_start = get_s();
		Eigen::VectorXf x = m_solver.solve(m_At * b);
		double solve_end = get_s();

		float* log_shading = x.data() + sz;
		reflect_clamp(m_w, m_h, x.data(), log_shading);

		std::vector<float> rgb_logimg(3*sz);
		log(im, rgb_logimg.data(), rgb_logimg.size());

		// do gray to rgb conversion
		// log R = log I - log S
		for (int i = 0; i < sz; ++i) {
			rgb_logimg[i * 3 + 0] = rgb_logimg[i * 3 + 0] - log_shading[i];
			rgb_logimg[i * 3 + 1] = rgb_logimg[i * 3 + 1] - log_shading[i];
			rgb_logimg[i * 3 + 2] = rgb_logimg[i * 3 + 2] - log_shading[i];
		}

		exp(rgb_logimg.data(), reflectance, 3 * sz);
		exp(log_shading, shading, sz);
	}
}


#define BOOST_TEST_MODULE "test_omp_bond_length_interaction"

#ifdef BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#else
#include <boost/test/included/unit_test.hpp>
#endif

#include <mjolnir/math/math.hpp>
#include <mjolnir/core/BoundaryCondition.hpp>
#include <mjolnir/core/SimulatorTraits.hpp>
#include <mjolnir/forcefield/local/HarmonicPotential.hpp>
#include <mjolnir/omp/System.hpp>
#include <mjolnir/omp/RandomNumberGenerator.hpp>
#include <mjolnir/omp/BondLengthInteraction.hpp>
#include <mjolnir/util/make_unique.hpp>

BOOST_AUTO_TEST_CASE(omp_BondLength_calc_force)
{
    constexpr double tol = 1e-8;
    mjolnir::LoggerManager::set_default_logger("test_omp_bond_length_interaction.log");

    using traits_type      = mjolnir::OpenMPSimulatorTraits<double, mjolnir::UnlimitedBoundary>;
    using real_type        = typename traits_type::real_type;
    using coordinate_type  = typename traits_type::coordinate_type;
    using boundary_type    = typename traits_type::boundary_type;
    using system_type      = mjolnir::System<traits_type>;
    using potential_type   = mjolnir::HarmonicPotential<real_type>;
    using interaction_type = mjolnir::BondLengthInteraction<traits_type, potential_type>;
    using rng_type         = mjolnir::RandomNumberGenerator<traits_type>;

    using sequencial_system_type      = mjolnir::System<
        mjolnir::SimulatorTraits<double, mjolnir::UnlimitedBoundary>>;
    using sequencial_interaction_type = mjolnir::BondLengthInteraction<
        mjolnir::SimulatorTraits<double, mjolnir::UnlimitedBoundary>, potential_type>;

    const int max_number_of_threads = omp_get_max_threads();
    BOOST_TEST_WARN(max_number_of_threads > 2);
    BOOST_TEST_MESSAGE("maximum number of threads = " << max_number_of_threads);

    for(int num_thread=1; num_thread<=max_number_of_threads; ++num_thread)
    {
        omp_set_num_threads(num_thread);
        BOOST_TEST_MESSAGE("maximum number of threads = " << omp_get_max_threads());

        potential_type   potential(/* k = */100.0, /* native length = */ 5.0);
        interaction_type interaction(/*topol = */"none", {
                {std::array<std::size_t, 2>{{0, 1}}, potential},
                {std::array<std::size_t, 2>{{1, 2}}, potential},
                {std::array<std::size_t, 2>{{2, 3}}, potential},
                {std::array<std::size_t, 2>{{3, 4}}, potential},
                {std::array<std::size_t, 2>{{4, 5}}, potential},
                {std::array<std::size_t, 2>{{5, 6}}, potential},
                {std::array<std::size_t, 2>{{6, 7}}, potential},
                {std::array<std::size_t, 2>{{7, 8}}, potential},
                {std::array<std::size_t, 2>{{8, 9}}, potential}
            });

        sequencial_interaction_type seq_interaction("none", {
                {std::array<std::size_t, 2>{{0, 1}}, potential},
                {std::array<std::size_t, 2>{{1, 2}}, potential},
                {std::array<std::size_t, 2>{{2, 3}}, potential},
                {std::array<std::size_t, 2>{{3, 4}}, potential},
                {std::array<std::size_t, 2>{{4, 5}}, potential},
                {std::array<std::size_t, 2>{{5, 6}}, potential},
                {std::array<std::size_t, 2>{{6, 7}}, potential},
                {std::array<std::size_t, 2>{{7, 8}}, potential},
                {std::array<std::size_t, 2>{{8, 9}}, potential}
            });

        rng_type    rng(123456789);
        system_type sys(10, boundary_type{});
        for(std::size_t i=0; i<sys.size(); ++i)
        {
            sys.mass(i)     = 1.0;
            sys.position(i) = mjolnir::math::make_coordinate<coordinate_type>(i*5.0, 0.0, 0.0);
            sys.velocity(i) = mjolnir::math::make_coordinate<coordinate_type>(0, 0, 0);
            sys.force(i)    = mjolnir::math::make_coordinate<coordinate_type>(0, 0, 0);
            sys.name(i)     = "X";
            sys.group(i)    = "TEST";
        }
        // add perturbation
        for(std::size_t i=0; i<sys.size(); ++i)
        {
            mjolnir::math::X(sys.position(i)) += rng.uniform_real(-0.1, 0.1);
            mjolnir::math::Y(sys.position(i)) += rng.uniform_real(-0.1, 0.1);
            mjolnir::math::Z(sys.position(i)) += rng.uniform_real(-0.1, 0.1);
        }

        // init sequential one with the same coordinates
        sequencial_system_type seq_sys(10, boundary_type{});
        for(std::size_t i=0; i<sys.size(); ++i)
        {
            seq_sys.mass(i)     = sys.mass(i);
            seq_sys.position(i) = sys.position(i);
            seq_sys.velocity(i) = sys.velocity(i);
            seq_sys.force(i)    = sys.force(i);
            seq_sys.name(i)     = sys.name(i);
            seq_sys.group(i)    = sys.group(i);
        }

        // calculate forces with openmp
        interaction.calc_force(sys);
        sys.postprocess_forces();

        // calculate forces without openmp
        seq_interaction.calc_force(seq_sys);

        // check the values are the same
        for(std::size_t i=0; i<sys.size(); ++i)
        {
            BOOST_TEST(mjolnir::math::X(seq_sys.force(i)) == mjolnir::math::X(sys.force(i)),
                       boost::test_tools::tolerance(tol));
            BOOST_TEST(mjolnir::math::Y(seq_sys.force(i)) == mjolnir::math::Y(sys.force(i)),
                       boost::test_tools::tolerance(tol));
            BOOST_TEST(mjolnir::math::Z(seq_sys.force(i)) == mjolnir::math::Z(sys.force(i)),
                       boost::test_tools::tolerance(tol));
        }
        BOOST_TEST(interaction.calc_energy(sys) == seq_interaction.calc_energy(seq_sys),
                   boost::test_tools::tolerance(tol));
    }
}

BOOST_AUTO_TEST_CASE(omp_BondLength_calc_force_and_energy)
{
    constexpr double tol = 1e-8;
    mjolnir::LoggerManager::set_default_logger("test_omp_bond_length_interaction.log");

    using traits_type      = mjolnir::OpenMPSimulatorTraits<double, mjolnir::UnlimitedBoundary>;
    using real_type        = typename traits_type::real_type;
    using coordinate_type  = typename traits_type::coordinate_type;
    using boundary_type    = typename traits_type::boundary_type;
    using system_type      = mjolnir::System<traits_type>;
    using potential_type   = mjolnir::HarmonicPotential<real_type>;
    using interaction_type = mjolnir::BondLengthInteraction<traits_type, potential_type>;
    using rng_type         = mjolnir::RandomNumberGenerator<traits_type>;

    const int max_number_of_threads = omp_get_max_threads();
    BOOST_TEST_WARN(max_number_of_threads > 2);
    BOOST_TEST_MESSAGE("maximum number of threads = " << max_number_of_threads);

    for(int num_thread=1; num_thread<=max_number_of_threads; ++num_thread)
    {
        omp_set_num_threads(num_thread);
        BOOST_TEST_MESSAGE("maximum number of threads = " << omp_get_max_threads());

        potential_type   potential(/* k = */100.0, /* native length = */ 5.0);
        interaction_type interaction(/*topol = */"none", {
                {std::array<std::size_t, 2>{{0, 1}}, potential},
                {std::array<std::size_t, 2>{{1, 2}}, potential},
                {std::array<std::size_t, 2>{{2, 3}}, potential},
                {std::array<std::size_t, 2>{{3, 4}}, potential},
                {std::array<std::size_t, 2>{{4, 5}}, potential},
                {std::array<std::size_t, 2>{{5, 6}}, potential},
                {std::array<std::size_t, 2>{{6, 7}}, potential},
                {std::array<std::size_t, 2>{{7, 8}}, potential},
                {std::array<std::size_t, 2>{{8, 9}}, potential}
            });

        rng_type    rng(123456789);
        system_type sys(10, boundary_type{});
        for(std::size_t i=0; i<sys.size(); ++i)
        {
            sys.mass(i)     = 1.0;
            sys.position(i) = mjolnir::math::make_coordinate<coordinate_type>(i*5.0, 0.0, 0.0);
            sys.velocity(i) = mjolnir::math::make_coordinate<coordinate_type>(0, 0, 0);
            sys.force(i)    = mjolnir::math::make_coordinate<coordinate_type>(0, 0, 0);
            sys.name(i)     = "X";
            sys.group(i)    = "TEST";
        }
        // add perturbation
        for(std::size_t i=0; i<sys.size(); ++i)
        {
            mjolnir::math::X(sys.position(i)) += rng.uniform_real(-0.1, 0.1);
            mjolnir::math::Y(sys.position(i)) += rng.uniform_real(-0.1, 0.1);
            mjolnir::math::Z(sys.position(i)) += rng.uniform_real(-0.1, 0.1);
        }

        interaction.initialize(sys);

        auto ref_sys = sys;

        // calculate forces with openmp
        const auto ref_ene = interaction.calc_energy(ref_sys);
        interaction.calc_force(ref_sys);
        ref_sys.postprocess_forces();

        const auto ene = interaction.calc_force_and_energy(sys);
        sys.postprocess_forces();

        BOOST_TEST(ene == ref_ene, boost::test_tools::tolerance(tol));
        for(std::size_t i=0; i<sys.size(); ++i)
        {
            BOOST_TEST(mjolnir::math::X(ref_sys.force(i)) == mjolnir::math::X(sys.force(i)), boost::test_tools::tolerance(tol));
            BOOST_TEST(mjolnir::math::Y(ref_sys.force(i)) == mjolnir::math::Y(sys.force(i)), boost::test_tools::tolerance(tol));
            BOOST_TEST(mjolnir::math::Z(ref_sys.force(i)) == mjolnir::math::Z(sys.force(i)), boost::test_tools::tolerance(tol));
        }

    }
}


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

#include <boost/hana/list/mcd.hpp>

#include <boost/hana/detail/assert.hpp>
#include <boost/hana/detail/constexpr.hpp>
#include <boost/hana/detail/minimal/list.hpp>

#include <boost/hana/integral.hpp>
using namespace boost::hana;

// group requires the elements to be compile-time comparable
template <int i>
constexpr auto x = int_<i>;

template <typename mcd>
void test() {
    BOOST_HANA_CONSTEXPR_LAMBDA auto list = detail::minimal::list<mcd>;

    BOOST_HANA_CONSTANT_ASSERT(group(list()) == list());

    BOOST_HANA_CONSTANT_ASSERT(group(list(x<0>)) == list(list(x<0>)));

    BOOST_HANA_CONSTANT_ASSERT(group(list(x<0>, x<0>)) == list(list(x<0>, x<0>)));
    BOOST_HANA_CONSTANT_ASSERT(group(list(x<0>, x<1>)) == list(list(x<0>), list(x<1>)));

    BOOST_HANA_CONSTANT_ASSERT(group(list(x<0>, x<0>, x<0>)) == list(list(x<0>, x<0>, x<0>)));
    BOOST_HANA_CONSTANT_ASSERT(group(list(x<0>, x<0>, x<1>)) == list(list(x<0>, x<0>), list(x<1>)));
    BOOST_HANA_CONSTANT_ASSERT(group(list(x<0>, x<1>, x<0>)) == list(list(x<0>), list(x<1>), list(x<0>)));
    BOOST_HANA_CONSTANT_ASSERT(group(list(x<1>, x<0>, x<0>)) == list(list(x<1>), list(x<0>, x<0>)));

    BOOST_HANA_CONSTANT_ASSERT(group(list(x<0>, x<0>, x<1>, x<1>)) == list(list(x<0>, x<0>), list(x<1>, x<1>)));
    BOOST_HANA_CONSTANT_ASSERT(group(list(x<0>, x<0>, x<1>, x<1>, x<2>, x<2>, x<2>)) == list(list(x<0>, x<0>), list(x<1>, x<1>), list(x<2>, x<2>, x<2>)));
}

int main() {
    test<List::mcd<void>>();
}


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

* GTSAM Copyright 2010, Georgia Tech Research Corporation,
* Atlanta, Georgia 30332-0415
* All Rights Reserved
* Authors: Frank Dellaert, et al. (see THANKS for the full author list)

* See LICENSE for the license information

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

/**
* @file ConcurrentCalibration.cpp
* @brief First step towards estimating monocular calibration in concurrent
* filter/smoother framework. To start with, just batch LM.
* @date June 11, 2014
* @author Chris Beall
*/

#include <gtsam/geometry/Pose3.h>
#include <gtsam/nonlinear/Values.h>
#include <gtsam/nonlinear/NonlinearEquality.h>
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
#include <gtsam/nonlinear/LevenbergMarquardtOptimizer.h>
#include <gtsam/inference/Symbol.h>
#include <gtsam/slam/ProjectionFactor.h>
#include <gtsam/slam/GeneralSFMFactor.h>
#include <gtsam/slam/PriorFactor.h>
#include <gtsam/slam/dataset.h>

#include <string>
#include <fstream>
#include <iostream>
#include <boost/lexical_cast.hpp>

using namespace std;
using namespace gtsam;

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

  Values initial_estimate;
  NonlinearFactorGraph graph;
  const noiseModel::Isotropic::shared_ptr model = noiseModel::Isotropic::Sigma(2,1);

  string calibration_loc = findExampleDataFile("VO_calibration00s.txt");
  string pose_loc = findExampleDataFile("VO_camera_poses00s.txt");
  string factor_loc = findExampleDataFile("VO_stereo_factors00s.txt");

  //read camera calibration info from file
  // focal lengths fx, fy, skew s, principal point u0, v0, baseline b
  double fx, fy, s, u0, v0, b;
  ifstream calibration_file(calibration_loc.c_str());
  cout << "Reading calibration info" << endl;
  calibration_file >> fx >> fy >> s >> u0 >> v0 >> b;

  //create stereo camera calibration object
  const Cal3_S2::shared_ptr K(new Cal3_S2(fx,fy,s,u0,v0));
  const Cal3_S2::shared_ptr noisy_K(new Cal3_S2(fx*1.2,fy*1.2,s,u0-10,v0+10));

  initial_estimate.insert(Symbol('K', 0), *noisy_K);

  noiseModel::Diagonal::shared_ptr calNoise = noiseModel::Diagonal::Sigmas((Vector(5) << 500, 500, 1e-5, 100, 100).finished());
  graph.push_back(PriorFactor<Cal3_S2>(Symbol('K', 0), *noisy_K, calNoise));

  ifstream pose_file(pose_loc.c_str());
  cout << "Reading camera poses" << endl;
  int pose_id;
  MatrixRowMajor m(4,4);
  //read camera pose parameters and use to make initial estimates of camera poses
  while (pose_file >> pose_id) {
    for (int i = 0; i < 16; i++) {
      pose_file >> m.data()[i];
    }
    initial_estimate.insert(Symbol('x', pose_id), Pose3(m));
  }

  noiseModel::Isotropic::shared_ptr poseNoise = noiseModel::Isotropic::Sigma(6, 0.01);
  graph.push_back(PriorFactor<Pose3>(Symbol('x', pose_id), Pose3(m), poseNoise));

  // camera and landmark keys
  size_t x, l;

  // pixel coordinates uL, uR, v (same for left/right images due to rectification)
  // landmark coordinates X, Y, Z in camera frame, resulting from triangulation
  double uL, uR, v, X, Y, Z;
  ifstream factor_file(factor_loc.c_str());
  cout << "Reading stereo factors" << endl;
  //read stereo measurement details from file and use to create and add GenericStereoFactor objects to the graph representation
  while (factor_file >> x >> l >> uL >> uR >> v >> X >> Y >> Z) {
//    graph.push_back( GenericStereoFactor<Pose3, Point3>(StereoPoint2(uL, uR, v), model, Symbol('x', x), Symbol('l', l), K));

    graph.push_back(GeneralSFMFactor2<Cal3_S2>(Point2(uL,v), model, Symbol('x', x), Symbol('l', l), Symbol('K', 0)));

    //if the landmark variable included in this factor has not yet been added to the initial variable value estimate, add it
    if (!initial_estimate.exists(Symbol('l', l))) {
      Pose3 camPose = initial_estimate.at<Pose3>(Symbol('x', x));
      //transform_from() transforms the input Point3 from the camera pose space, camPose, to the global space
      Point3 worldPoint = camPose.transform_from(Point3(X, Y, Z));
      initial_estimate.insert(Symbol('l', l), worldPoint);
    }
  }

  Pose3 first_pose = initial_estimate.at<Pose3>(Symbol('x',1));
  //constrain the first pose such that it cannot change from its original value during optimization
  // NOTE: NonlinearEquality forces the optimizer to use QR rather than Cholesky
  // QR is much slower than Cholesky, but numerically more stable
  graph.push_back(NonlinearEquality<Pose3>(Symbol('x',1),first_pose));

  cout << "Optimizing" << endl;
  LevenbergMarquardtParams params;
  params.verbosityLM = LevenbergMarquardtParams::TRYLAMBDA;
  params.verbosity = NonlinearOptimizerParams::ERROR;

  //create Levenberg-Marquardt optimizer to optimize the factor graph
  LevenbergMarquardtOptimizer optimizer(graph, initial_estimate,params);
//  Values result = optimizer.optimize();

  string K_values_file = "K_values.txt";
  ofstream stream_K(K_values_file.c_str());

  double currentError;

  stream_K << optimizer.iterations() << " " << optimizer.values().at<Cal3_S2>(Symbol('K',0)).vector().transpose() << endl;

  // Iterative loop
  do {
    // Do next iteration
    currentError = optimizer.error();
    optimizer.iterate();

    stream_K << optimizer.iterations() << " " << optimizer.values().at<Cal3_S2>(Symbol('K',0)).vector().transpose() << endl;

    if(params.verbosity >= NonlinearOptimizerParams::ERROR) cout << "newError: " << optimizer.error() << endl;
  } while(optimizer.iterations() < params.maxIterations &&
      !checkConvergence(params.relativeErrorTol, params.absoluteErrorTol,
            params.errorTol, currentError, optimizer.error(), params.verbosity));

  Values result = optimizer.values();

  cout << "Final result sample:" << endl;
  Values pose_values = result.filter<Pose3>();
  pose_values.print("Final camera poses:\n");

  Values(result.filter<Cal3_S2>()).print("Final K\n");

  noisy_K->print("Initial noisy K\n");
  K->print("Initial correct K\n");

  return 0;
}


#include <cstdlib>
#include <iostream>
#include <chrono>
#include <boost/program_options.hpp>

#include "Graph.hpp"
#include "Clique.hpp"
#include "IndependentSet.hpp"

using namespace std;
using namespace std::chrono;
namespace po = boost::program_options;

void show_help(const po::options_description& desc, const std::string& topic = ""){
    std::cout << desc << '\n';
    if (topic != "") {
        std::cout << "You asked for help on: " << topic << '\n';
    }
    exit( EXIT_SUCCESS );
}

bool argParse(int argc, char* argv[], string* method, string* path, bool* printClique){
    po::options_description desc("Usage");
    desc.add_options()
        ("help,h", po::value< std::string >()->implicit_value("")->notifier(
            [&desc](const std::string& topic) {
                show_help(desc, topic);
            }
        ),"Show help.")
        ("instance,i", po::value<string>(), "Instance path")
        ("method,m", po::value<string>()->default_value("auto"), "(Optional) Specify to method to use, may be <auto>, <mis> or <clique>")
        ("print,p", "(Optional) If enabled program will print all vertex in maximum clique");

    if (argc == 1) {
        show_help(desc);
        return false;
    }

    po::variables_map args;

    try {
        po::store(po::parse_command_line(argc, argv, desc), args);
    } catch (po::error const& e) {
        std::cerr << e.what() << '\n';
        exit( EXIT_FAILURE );
    }
    po::notify(args);

    *method = args["method"].as<string>();

    if(args.count("print")){
        *printClique = true;
    }

    if(args.count("instance")){
        *path = args["instance"].as<string>();
    } else {
        cerr << "Instance parameter is required" << endl << endl;
        show_help(desc);
        return false;
    }

    return true;
}

int main(int argc, char** argv){
    string method = "";
    string path = "";
    bool printClique = false;

    if(!argParse(argc, argv, &method, &path, &printClique)){
        return 1;
    }

    Graph graph(path);

    high_resolution_clock::time_point t1 = high_resolution_clock::now();

        Clique clique(graph);
        //int max = clique.searchLargestClique();
        IndependentSet is(graph);
        int max = is.searchLargestIndependentSet();

    high_resolution_clock::time_point t2 = high_resolution_clock::now();
    duration<double> time_span = duration_cast<duration<double> >(t2 - t1);

    cout << max << "\t" << time_span.count() << endl;

    if(printClique){
        auto it = clique.getLargestCliqueBegin();
        for(;it != clique.getLargestCliqueEnd() ; it++){
            cout << *it << " ";
        }
        cout << endl;
    }

    return 0;
}


//=======================================================================
// Copyright 2009 Trustees of Indiana University.
// Authors: Michael Hansen
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include <iostream>
#include <map>
#include <vector>

#include <boost/lexical_cast.hpp>
#include <boost/random.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/dijkstra_shortest_paths_no_color_map.hpp>
#include <boost/graph/properties.hpp>
#include <boost/graph/random.hpp>
#include <boost/test/minimal.hpp>
#include <boost/graph/iteration_macros.hpp>

#define INITIALIZE_VERTEX 0
#define DISCOVER_VERTEX 1
#define EXAMINE_VERTEX 2
#define EXAMINE_EDGE 3
#define EDGE_RELAXED 4
#define EDGE_NOT_RELAXED 5
#define FINISH_VERTEX 6

template <typename Graph>
void run_dijkstra_test(const Graph& graph)
{
  using namespace boost;

  // Set up property maps
  typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;

  typedef typename std::map<vertex_t, vertex_t> vertex_map_t;
  typedef associative_property_map<vertex_map_t> predecessor_map_t;
  vertex_map_t default_vertex_map, no_color_map_vertex_map;
  predecessor_map_t default_predecessor_map(default_vertex_map),
                    no_color_map_predecessor_map(no_color_map_vertex_map);

  typedef typename std::map<vertex_t, double> vertex_double_map_t;
  typedef associative_property_map<vertex_double_map_t> distance_map_t;
  vertex_double_map_t default_vertex_double_map, no_color_map_vertex_double_map;
  distance_map_t default_distance_map(default_vertex_double_map),
                 no_color_map_distance_map(no_color_map_vertex_double_map);

  // Run dijkstra algoirthms
  dijkstra_shortest_paths(graph, vertex(0, graph),
                          predecessor_map(default_predecessor_map)
                          .distance_map(default_distance_map));

  dijkstra_shortest_paths_no_color_map(graph, vertex(0, graph),
                                       predecessor_map(no_color_map_predecessor_map)
                                       .distance_map(no_color_map_distance_map));

  // Verify that predecessor maps are equal
  BOOST_CHECK(std::equal(default_vertex_map.begin(), default_vertex_map.end(),
              no_color_map_vertex_map.begin()));

  // Verify that distance maps are equal
  BOOST_CHECK(std::equal(default_vertex_double_map.begin(), default_vertex_double_map.end(),
              no_color_map_vertex_double_map.begin()));
}

int test_main(int argc, char* argv[])
{
  using namespace boost;

  int vertices_to_create = 10;
  int edges_to_create = 500;
  std::size_t random_seed = time(0);

  if (argc > 1) {
    vertices_to_create = lexical_cast<int>(argv[1]);
  }

  if (argc > 2) {
    edges_to_create = lexical_cast<int>(argv[2]);
  }

  if (argc > 3) {
    random_seed = lexical_cast<std::size_t>(argv[3]);
  }

  minstd_rand generator(random_seed);

  // Set up graph
  typedef adjacency_list<listS, listS, directedS,
    property<vertex_index_t, int >,
    property<edge_weight_t, double> > graph_t;

  graph_t graph;
  generate_random_graph(graph, vertices_to_create, edges_to_create, generator);

  // Set up property maps
  typedef property_map<graph_t, vertex_index_t>::type index_map_t;
  index_map_t index_map = get(vertex_index, graph);
  int vertex_index = 0;

  BGL_FORALL_VERTICES(current_vertex, graph, graph_t) {
    put(index_map, current_vertex, vertex_index++);
  }

  randomize_property<edge_weight_t>(graph, generator);

  // Run comparison test with original dijkstra_shortest_paths
  std::cout << "Running dijkstra shortest paths test with " << num_vertices(graph) <<
    " vertices and " << num_edges(graph) << " edges " << std::endl;

  run_dijkstra_test(graph);

  return 0;
}


#ifdef COMPILATION// -*-indent-tabs-mode:t;c-basic-offset:4;tab-width:4-*-
$CXX $0 -o $0x -DMULTI_ACCESS_NDEBUG -lboost_unit_test_framework&&$0x&&rm $0x;exit
#endif
// © Alfredo A. Correa 2021

#define BOOST_TEST_MODULE "C++ Unit Tests for Multi element access"
#define BOOST_TEST_DYN_LINK
#include<boost/test/unit_test.hpp>

#include "../array.hpp"

#include <boost/hana/integral_constant.hpp>
#include <boost/iterator/transform_iterator.hpp>

#include<cassert>
#include<iostream>
#include<vector>

namespace hana = boost::hana;

////https://stackoverflow.com/a/35110453/225186
//template<class T>constexpr std::remove_reference_t<T> const_aux(T&&t){return t;}
//template<bool b> struct logic_assert_aux;
//template<> struct logic_assert_aux<true>{
//	template<class T> static constexpr void _(T&& cond){static_assert(cond, "!");}
//};
//template<> struct logic_assert_aux<false>{
//	template<class T> static constexpr void _(T&& cond){assert(cond && "!");}
//};
//#if (not defined(__INTEL_COMPILER)) and (not defined(__NVCC__))
//#define logic_assert(ConD, MsG) logic_assert_aux<noexcept(const_aux(ConD))>::_(ConD);
//#else
//#define logic_assert(ConD, MsG) assert(ConD)
//#endif
#if 0
if constexpr(noexcept(const_aux(ConD))) static_assert(ConD, MsG);\
	else assert(ConD && MsG);
#endif

template<typename Integral, Integral const n>
struct integral_constant : private hana::integral_constant<Integral, n>{
//	using hana::integral_constant<Integral, n>::integral_constant;
	constexpr explicit operator Integral const&() const{
		return hana::integral_constant<Integral, n>::value;
	}
	integral_constant() = default;
	constexpr explicit integral_constant(Integral const& i){
		assert(i == n);
	}
	constexpr auto operator==(Integral const& o) const{return static_cast<Integral const&>(*this)==o;}
	constexpr auto operator==(integral_constant const&/*other*/){return std::true_type{};}
	template<Integral n2, typename = std::enable_if_t<n2!=n> >
	constexpr auto operator==(integral_constant<Integral, n2> const&/*other*/){return std::false_type{};}
	template<Integral n2>
	friend constexpr auto operator+(integral_constant const&/*a*/, integral_constant<Integral, n2> const&/*b*/){
		return integral_constant<Integral, hana::integral_constant<Integral, n>::value + n2>{};
	}
	template<Integral n2>
	friend constexpr auto operator-(integral_constant const&/*a*/, integral_constant<Integral, n2> const&/*b*/){
		return integral_constant<Integral, hana::integral_constant<Integral, n>::value - n2>{};
	}
//	constexpr auto operator=(Integral other) -> integral_constant&{assert(other == n); return *this;}
	friend constexpr auto operator>=(Integral const& a, integral_constant const&/*self*/){return a >= n;}
	friend constexpr auto operator<(Integral const& a, integral_constant const&/*self*/){return a < n;}
};

namespace multi = boost::multi;

BOOST_AUTO_TEST_CASE(multi_range){

#if defined(__cpp_deduction_guides) and __cpp_deduction_guides
	BOOST_REQUIRE(( multi::range{5, 5}.empty() ));
#else
	BOOST_REQUIRE(( multi::range<std::ptrdiff_t>{5, 5}.empty() ));
#endif
{
	auto r = multi::range<std::ptrdiff_t>{5, 10};
	std::vector<double> v(r.begin(), r.end());
	BOOST_REQUIRE( v[1] == 6 );
}
{
	auto r = multi::range<std::ptrdiff_t>{5, 10};
	auto f = [](auto x){return x+1;};
	std::vector<double> v(
		boost::make_transform_iterator(r.begin(), f),
		boost::make_transform_iterator(r.end()  , f)
	);
	BOOST_REQUIRE( v[1] == 7 );
}
}

BOOST_AUTO_TEST_CASE(multi_range_with_hana_literals){
//	using namespace hana::literals; // contains the _c suffix

	static_assert(( integral_constant<int, 1234>{} == 1234 ), "!");
	static_assert(( (integral_constant<int, 1234>{} + integral_constant<int, 1>{})  == 1235 ), "!");
	static_assert(( (integral_constant<int, 1234>{} + integral_constant<int, 1>{})  == integral_constant<int, 1235>{} ), "!");
#if defined(__cpp_deduction_guides) and __cpp_deduction_guides
	static_assert(( multi::range{integral_constant<int, 0>{}, integral_constant<int, 5>{}}.size() == integral_constant<int, 5>{} ), "!");
	static_assert(( size(multi::range{integral_constant<int, 0>{}, integral_constant<int, 5>{}}) == integral_constant<int, 5>{} ), "!");
	integral_constant<int, 5> five; five = 5;
#endif
}

BOOST_AUTO_TEST_CASE(multi_range_in_constexpr){
	BOOST_REQUIRE( multi::extension_t<int>{5} == 5 ); // this is not a constexpr in cuda 10
	BOOST_REQUIRE(( multi::extension_t<int>{5, 12}.contains(10) ));

	multi::range<int> rr{5, 12};
	BOOST_REQUIRE( rr.contains(6) );
	BOOST_REQUIRE( not rr.contains(12) );
}


// Example program for passing arguments from signal invocations to slots.
//
// Copyright Douglas Gregor 2001-2004.
// Copyright Frank Mori Hess 2009.
//
// Use, modification and
// distribution is subject to the Boost Software License, Version
// 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// For more information, see http://www.boost.org

#include <iostream>
#include <boost/signals2/signal.hpp>

//[ slot_arguments_slot_defs_code_snippet
void print_args(float x, float y)
{
  std::cout << "The arguments are " << x << " and " << y << std::endl;
}

void print_sum(float x, float y)
{
  std::cout << "The sum is " << x + y << std::endl;
}

void print_product(float x, float y)
{
  std::cout << "The product is " << x * y << std::endl;
}

void print_difference(float x, float y)
{
  std::cout << "The difference is " << x - y << std::endl;
}

void print_quotient(float x, float y)
{
  std::cout << "The quotient is " << x / y << std::endl;
}
//]

int main()
{
//[ slot_arguments_main_code_snippet
  boost::signals2::signal<void (float, float)> sig;

  sig.connect(&print_args);
  sig.connect(&print_sum);
  sig.connect(&print_product);
  sig.connect(&print_difference);
  sig.connect(&print_quotient);

  sig(5., 3.);
//]
  return 0;
}


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

/*
 Copyright (C) 2014 Jose Aparicio

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

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

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

#include <ql/quantlib.hpp>

#include <boost/timer.hpp>
#include <boost/make_shared.hpp>
#include <boost/function.hpp>

#include <iostream>
#include <iomanip>

using namespace std;
using namespace QuantLib;

#ifdef BOOST_MSVC
#  ifdef QL_ENABLE_THREAD_SAFE_OBSERVER_PATTERN
#    include <ql/auto_link.hpp>
#    define BOOST_LIB_NAME boost_system
#    include <boost/config/auto_link.hpp>
#    undef BOOST_LIB_NAME
#    define BOOST_LIB_NAME boost_thread
#    include <boost/config/auto_link.hpp>
#    undef BOOST_LIB_NAME
#  endif
#endif

#if defined(QL_ENABLE_SESSIONS)
namespace QuantLib {

    Integer sessionId() { return 0; }

}
#endif

/* This sample code shows basic usage of a Latent variable model.
   The data and correlation problem presented is the same as in:
     'Modelling Dependent Defaults: Asset Correlations Are Not Enough!'
     Frey R., A. J. McNeil and M. A. Nyfeler RiskLab publications March 2001
*/
int main(int, char* []) {

    try {

        boost::timer timer;
        std::cout << std::endl;

        Calendar calendar = TARGET();
        Date todaysDate(19, March, 2014);
        // must be a business day
        todaysDate = calendar.adjust(todaysDate);

        Settings::instance().evaluationDate() = todaysDate;

        /* --------------------------------------------------------------
                        SET UP BASKET PORTFOLIO
        -------------------------------------------------------------- */
        // build curves and issuers into a basket of three names
        std::vector<Real> hazardRates(3, -std::log(1.-0.01));
        std::vector<std::string> names;
        for(Size i=0; i<hazardRates.size(); i++)
            names.push_back(std::string("Acme") +
                boost::lexical_cast<std::string>(i));
        std::vector<Handle<DefaultProbabilityTermStructure> > defTS;
        for(Size i=0; i<hazardRates.size(); i++)
            defTS.push_back(Handle<DefaultProbabilityTermStructure>(
                boost::make_shared<FlatHazardRate>(0, TARGET(), hazardRates[i],
                    Actual365Fixed())));
        std::vector<Issuer> issuers;
        for(Size i=0; i<hazardRates.size(); i++) {
            std::vector<QuantLib::Issuer::key_curve_pair> curves(1,
                std::make_pair(NorthAmericaCorpDefaultKey(
                    EURCurrency(), QuantLib::SeniorSec,
                    Period(), 1. // amount threshold
                    ), defTS[i]));
            issuers.push_back(Issuer(curves));
        }

        boost::shared_ptr<Pool> thePool = boost::make_shared<Pool>();
        for(Size i=0; i<hazardRates.size(); i++)
            thePool->add(names[i], issuers[i], NorthAmericaCorpDefaultKey(
                    EURCurrency(), QuantLib::SeniorSec, Period(), 1.));

        std::vector<DefaultProbKey> defaultKeys(hazardRates.size(),
            NorthAmericaCorpDefaultKey(EURCurrency(), SeniorSec, Period(), 1.));
        // Recoveries are irrelevant in this example but must be given as the
        //   lib stands.
        std::vector<boost::shared_ptr<RecoveryRateModel> > rrModels(
            hazardRates.size(), boost::make_shared<ConstantRecoveryModel>(
            ConstantRecoveryModel(0.5, SeniorSec)));
        boost::shared_ptr<Basket> theBskt = boost::make_shared<Basket>(
            todaysDate, names, std::vector<Real>(hazardRates.size(), 100.),
            thePool);
        /* --------------------------------------------------------------
                        SET UP JOINT DEFAULT EVENT LATENT MODELS
        -------------------------------------------------------------- */
        // Latent model factors, corresponds to the first entry in Table1 of the
        //   publication mentioned. It is a single factor model
        std::vector<std::vector<Real> > fctrsWeights(hazardRates.size(),
            std::vector<Real>(1, std::sqrt(0.1)));
        // --- Default Latent models -------------------------------------
        // Gaussian integrable joint default model:
        boost::shared_ptr<GaussianDefProbLM> lmG(new
            GaussianDefProbLM(fctrsWeights,
            LatentModelIntegrationType::GaussianQuadrature,
			GaussianCopulaPolicy::initTraits() // otherwise gcc screams
			));
        // Define StudentT copula
        // this is as far as we can be from the Gaussian, 2 T_3 factors:
        std::vector<Integer> ordersT(2, 3);
        TCopulaPolicy::initTraits iniT;
        iniT.tOrders = ordersT;
        // StudentT integrable joint default model:
        boost::shared_ptr<TDefProbLM> lmT(new TDefProbLM(fctrsWeights,
            // LatentModelIntegrationType::GaussianQuadrature,
            LatentModelIntegrationType::Trapezoid,
            iniT));

        // --- Default Loss models ----------------------------------------
        // Gaussian random joint default model:
        Size numSimulations = 100000;
        // Size numCoresUsed = 4;
        // Sobol, many cores
        boost::shared_ptr<DefaultLossModel> rdlmG(
            boost::make_shared<RandomDefaultLM<GaussianCopulaPolicy> >(lmG,
                std::vector<Real>(), numSimulations, 1.e-6, 2863311530));
        // StudentT random joint default model:
        boost::shared_ptr<DefaultLossModel> rdlmT(
            boost::make_shared<RandomDefaultLM<TCopulaPolicy> >(lmT,
            std::vector<Real>(), numSimulations, 1.e-6, 2863311530));

        /* --------------------------------------------------------------
                        DUMP SOME RESULTS
        -------------------------------------------------------------- */
        /* Default correlations in a T copula should be below those of the
        gaussian for the same factors.
        The calculations on the MC show dispersion on both copulas (thats
        ok) and too large values with very large dispersions on the T case.
        Computations are ok, within the dispersion, for the gaussian; compare
        with the direct integration in both cases.
        However the T does converge to the gaussian value for large value of
        the parameters.
        */
        Date calcDate(TARGET().advance(Settings::instance().evaluationDate(),
            Period(120, Months)));
        std::vector<Probability> probEventsTLatent, probEventsGLatent,
            probEventsTRandLoss, probEventsGRandLoss;
        //
        lmT->resetBasket(theBskt);
        for(Size numEvts=0; numEvts <=theBskt->size(); numEvts++) {
            probEventsTLatent.push_back(lmT->probAtLeastNEvents(numEvts,
                calcDate));
         }
        //
        lmG->resetBasket(theBskt);
        for(Size numEvts=0; numEvts <=theBskt->size(); numEvts++) {
            probEventsGLatent.push_back(lmG->probAtLeastNEvents(numEvts,
                calcDate));
         }
        //
        theBskt->setLossModel(rdlmT);
        for(Size numEvts=0; numEvts <=theBskt->size(); numEvts++) {
            probEventsTRandLoss.push_back(theBskt->probAtLeastNEvents(numEvts,
                calcDate));
         }
        //
        theBskt->setLossModel(rdlmG);
        for(Size numEvts=0; numEvts <=theBskt->size(); numEvts++) {
            probEventsGRandLoss.push_back(theBskt->probAtLeastNEvents(numEvts,
                calcDate));
         }

        Date correlDate = TARGET().advance(
            Settings::instance().evaluationDate(), Period(12, Months));
        std::vector<std::vector<Real> > correlsGlm, correlsTlm, correlsGrand,
            correlsTrand;
        //
        lmG->resetBasket(theBskt);
        for(Size iName1=0; iName1 <theBskt->size(); iName1++) {
            std::vector<Real> tmp;
            for(Size iName2=0; iName2 <theBskt->size(); iName2++)
                tmp.push_back(lmG->defaultCorrelation(correlDate,
                    iName1, iName2));
            correlsGlm.push_back(tmp);
        }
        //
        lmT->resetBasket(theBskt);
        for(Size iName1=0; iName1 <theBskt->size(); iName1++) {
            std::vector<Real> tmp;
            for(Size iName2=0; iName2 <theBskt->size(); iName2++)
                tmp.push_back(lmT->defaultCorrelation(correlDate,
                    iName1, iName2));
            correlsTlm.push_back(tmp);
        }
        //
        theBskt->setLossModel(rdlmG);
        for(Size iName1=0; iName1 <theBskt->size(); iName1++) {
            std::vector<Real> tmp;
            for(Size iName2=0; iName2 <theBskt->size(); iName2++)
                tmp.push_back(theBskt->defaultCorrelation(correlDate,
                    iName1, iName2));
            correlsGrand.push_back(tmp);
        }
        //
        theBskt->setLossModel(rdlmT);
        for(Size iName1=0; iName1 <theBskt->size(); iName1++) {
            std::vector<Real> tmp;
            for(Size iName2=0; iName2 <theBskt->size(); iName2++)
                tmp.push_back(theBskt->defaultCorrelation(correlDate,
                    iName1, iName2));
            correlsTrand.push_back(tmp);
        }

        std::cout <<
            " Gaussian versus T prob of extreme event (random and integrable)-"
            << std::endl;
        for(Size numEvts=0; numEvts <=theBskt->size(); numEvts++) {
            std::cout << "-Prob of " << numEvts << " events... " <<
                probEventsGLatent[numEvts] << " ** " <<
                probEventsTLatent[numEvts] << " ** " <<
                probEventsGRandLoss[numEvts]<< " ** " <<
                probEventsTRandLoss[numEvts]
            << std::endl;
        }

        cout << endl;
        cout << "-- Default correlations G,T,GRand,TRand--" << endl;
        cout << "-----------------------------------------" << endl;
        for(Size iName1=0; iName1 <theBskt->size(); iName1++) {
            for(Size iName2=0; iName2 <theBskt->size(); iName2++)
                cout <<
                    correlsGlm[iName1][iName2] << " , ";
            ;
                cout << endl;
        }
        cout << endl;
        for(Size iName1=0; iName1 <theBskt->size(); iName1++) {
            for(Size iName2=0; iName2 <theBskt->size(); iName2++)
                cout <<
                    correlsTlm[iName1][iName2] << " , ";
            ;
                cout << endl;
        }
        cout << endl;
        for(Size iName1=0; iName1 <theBskt->size(); iName1++) {
            for(Size iName2=0; iName2 <theBskt->size(); iName2++)
                cout <<
                    correlsGrand[iName1][iName2] << " , ";
            ;
                cout << endl;
        }
        cout << endl;
        for(Size iName1=0; iName1 <theBskt->size(); iName1++) {
            for(Size iName2=0; iName2 <theBskt->size(); iName2++)
                cout <<
                    correlsTrand[iName1][iName2] << " , ";
            ;
                cout << endl;
        }

        Real seconds  = timer.elapsed();
        Integer hours = Integer(seconds/3600);
        seconds -= hours * 3600;
        Integer minutes = Integer(seconds/60);
        seconds -= minutes * 60;
        cout << "Run completed in ";
        if (hours > 0)
            cout << hours << " h ";
        if (hours > 0 || minutes > 0)
            cout << minutes << " m ";
        cout << fixed << setprecision(0)
             << seconds << " s" << endl;

        return 0;
    } catch (exception& e) {
        cerr << e.what() << endl;
        return 1;
    } catch (...) {
        cerr << "unknown error" << endl;
        return 1;
    }
}