// STL includes
#include <iostream>
#include <vector>
#include <unordered_set>
#include <climits>

// BGL includes
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/maximum_weighted_matching.hpp>
#include <boost/graph/max_cardinality_matching.hpp>

typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS> graph;
typedef boost::graph_traits<graph>::vertex_descriptor vertex_desc;

using namespace std;

int maximum_matching(const graph &G) {
  int n = boost::num_vertices(G);
  std::vector<vertex_desc> mate_map(n);  // exterior property map
  // const vertex_desc NULL_VERTEX = boost::graph_traits<weighted_graph>::null_vertex();

  boost::edmonds_maximum_cardinality_matching(G,
    boost::make_iterator_property_map(mate_map.begin(), boost::get(boost::vertex_index, G)));
  int matching_size = boost::matching_size(G,
    boost::make_iterator_property_map(mate_map.begin(), boost::get(boost::vertex_index, G)));
  return matching_size;
  // int min_weight = INT_MAX;
  // edge_desc e;
  // for (int i = 0; i < n; ++i) {
  //   // mate_map[i] != NULL_VERTEX: the vertex is matched
  //   // i < mate_map[i]: visit each edge in the matching only once
  //   // cout << i << " " << mate_map[i] << endl;
  //   e = edge(i,mate_map[i],G).first;
  //   min_weight = min(min_weight, weights[e]);
  // }
  // return min_weight;
}

void testcase()
{
  int n; cin >> n;
  int c; cin >> c;
  int f; cin >> f;
  graph G(n);
  vector<unordered_set<string>> characteristics(n);
  for(int i = 0; i < n; i++) {
    characteristics[i] = unordered_set<string>(0);
    for(int k = 0; k < c; k++) {
      string s; cin >> s;
      characteristics[i].insert(s);
    }
    for(int j = 0; j < i; j++) {
      int common = 0;
      for(auto s : characteristics[j]) {
        if(characteristics[i].count(s) > 0) common++;
      }
      // cout << i << " " << j << " " << common << " ";
      if(common > f) {
        // cout << i << " " << j << endl;
        boost::add_edge(i, j, G);
      }
      // cout << e << " " << weights[e] << endl;
    }
  }

  int matching_size = maximum_matching(G);
  // std::cout << matching_size << endl;
  if(matching_size == n / 2) {
    cout << "not optimal" << endl;
  } else {
    cout << "optimal" << endl;
  }
}

int main() {
  std::ios_base::sync_with_stdio(false); // Always!
  int t; cin >> t;
  while(t--) testcase();
}


#define __USE_MATH_DEFINES
#include <cmath>
#include <Eigen/Dense>
#include "../matplotlibcpp.h"
namespace plt = matplotlibcpp;

void waves(const unsigned n) {
  Eigen::MatrixXd X(n, n);
  for (unsigned i = 0; i < n; ++i) {
    for (unsigned j = 0; j < n; ++j) {
      X(i, j) = sin(3.0 * M_PI * i / n) * cos(20.0 * M_PI * j / n);
    }
  }
  plt::figure();
  plt::imshow(X, {{"cmap", "Spectral"}});
  plt::colorbar();
  plt::show();
}

int main() {
  waves(200);
  return 0;
}


#include <iostream>
#include <string>
#include <vector>
#include <boost/multiprecision/cpp_int.hpp>

using boost::multiprecision::cpp_int;

std::vector<cpp_int> take_input();

struct linear_combination {
    cpp_int quotient = 0;
    cpp_int x = 0;
    cpp_int y = 0;
};

void euclidean_gcd(cpp_int a, cpp_int b, std::vector<linear_combination> &combinations);

cpp_int
solve_congruence_system(cpp_int a, cpp_int m, cpp_int b, cpp_int n);

int main() {
    std::vector<cpp_int> input = take_input();

    cpp_int x = 0;
    cpp_int product = 1;
    std::cout << input.size() << std::endl;

    for (int i = 0; i < input.size(); i++) {
        std::cout << i << std::endl;
        cpp_int &m = input.at(i);
        x = solve_congruence_system(x, product, m - i, m);
        product *= m;
        while (x > product) {
            x -= product;
        }
        while (x < 0) {
            x += product;
        }
        std::cout << x % 17 << std::endl;
    }

    std::cout << x << std::endl;

//    100 - 6 * 15 = 10
//    15 - 1 * (100 - 6 * 15) = 5
//    15 + 6 * 15
    return 0;
}

void euclidean_gcd(cpp_int a, cpp_int b, std::vector<linear_combination> &combinations) {
    if (b % a == 0) {
        return;
    } else {
        linear_combination temp;
        temp.quotient = b / a;
        if (combinations.empty()) {
            temp.x = -temp.quotient;
            temp.y = 1;
        } else if (combinations.size() == 1) {
            temp.x = 1 + combinations.at(0).quotient * temp.quotient;
            temp.y = -temp.quotient;
        } else {
            temp.x = combinations.at(combinations.size() - 2).x -
                     temp.quotient * (combinations.at(combinations.size() - 1).x);
            temp.y = combinations.at(combinations.size() - 2).y -
                     temp.quotient * (combinations.at(combinations.size() - 1).y);
        }
        combinations.push_back(temp);
        euclidean_gcd(b % a, a, combinations);
        return;
    }
}

cpp_int solve_congruence_system(cpp_int a, cpp_int m, cpp_int b, cpp_int n) {

    std::vector<linear_combination> combinations;
    euclidean_gcd(m, n, combinations);

    cpp_int s = 0;
    cpp_int t = 0;
    if (!combinations.empty()) {
        s = combinations.at(combinations.size() - 1).x;
        t = combinations.at(combinations.size() - 1).y;
    }

    cpp_int x = b;
    while (x > m * n) {
        x -= m * n;
    }
    x *= m;
    while (x > m * n) {
        x -= m * n;
    }
    x *= s;
    while (x > m * n) {
        x -= m * n;
    }

    cpp_int y = a;
    while (y > m * n) {
        y -= m * n;
    }
    y *= n;
    while (y > m * n) {
        y -= m * n;
    }
    y *= t;
    y -= (y - m * n) / (m * n) * (m * n);
    while (y > m * n) {
        y -= m * n;
    }

    cpp_int z = x + y;
    while (z < 0) {
        z += m * n;
    }

    return x + y;
}

std::vector<cpp_int> take_input() {
    std::string line;
    std::string number;
    std::vector<cpp_int> numbers;
    std::cin.ignore(10000, '\n');
    std::getline(std::cin, line);

    for (char c : line) {
        if (c != ',') {
            number.append(1, c);
        } else {
            if (number == "x") {
                numbers.push_back(1);
            } else {
                numbers.push_back(stoi(number));
            }
            number = "";
        }
    }

    if (number == "x") {
        numbers.push_back(1);
    } else {
        numbers.push_back(stoi(number));
    }

    return numbers;
}

// 5 = 1 * 3 + 2
// 3 = 1 * 2 + 1
// 2 = 1 * 1 + 1
// 1 = 1 * 1


/**
 * @file   quaternion_algebra.hpp
 * @author Paul Furgale <paul.furgale@utoronto.ca>
 * @date   Sun Nov 21 19:20:37 2010
 *
 * @brief  Quaternion algebra from the paper Barfoot T D, Forbes J R, and Furgale P T. “Pose Estimation using Linearized
 * Rotations and Quaternion Algebra”. Acta Astronautica, 2010. doi:10.1016/j.actaastro.2010.06.049.
 *
 *
 */

#ifndef SM_QUATERNION_ALGEBRA_HPP
#define SM_QUATERNION_ALGEBRA_HPP
#include <Eigen/Core>
#include <Eigen/Geometry>

namespace sm {
namespace kinematics {

Eigen::Matrix3d quat2r(Eigen::Vector4d const& q);
Eigen::Vector4d r2quat(Eigen::Matrix3d const& C);
Eigen::Vector4d r2quat(Eigen::Matrix3d const& C);
Eigen::Vector4d axisAngle2quat(Eigen::Vector3d const& a);

template <typename Scalar_>
Eigen::Matrix<Scalar_, 3, 1> quat2AxisAngle(Eigen::Matrix<Scalar_, 4, 1> const& q);
extern template Eigen::Matrix<double, 3, 1> quat2AxisAngle(Eigen::Matrix<double, 4, 1> const& q);
extern template Eigen::Matrix<float, 3, 1> quat2AxisAngle(Eigen::Matrix<float, 4, 1> const& q);
inline Eigen::Vector3d quat2AxisAngle(Eigen::Vector4d const& q) { return quat2AxisAngle<>(q); }

Eigen::Matrix4d quatPlus(Eigen::Vector4d const& q);
Eigen::Vector4d qplus(Eigen::Vector4d const& q, Eigen::Vector4d const& p);
Eigen::Matrix4d quatOPlus(Eigen::Vector4d const& q);
Eigen::Vector4d qoplus(Eigen::Vector4d const& q, Eigen::Vector4d const& p);
Eigen::Vector4d quatInv(Eigen::Vector4d const& q);
Eigen::Vector3d quatRotate(Eigen::Vector4d const& q_a_b, Eigen::Vector3d const& v_b);
Eigen::Vector4d quatRandom();
Eigen::Vector4d quatIdentity();
void invertQuat(Eigen::Vector4d& q);
Eigen::Vector3d qeps(Eigen::Vector4d const& q);
double qeta(Eigen::Vector4d const& q);
// For estimation functions to handle a constraint-sensitive minimal parameterization for a quaternion update
Eigen::Matrix<double, 4, 3> quatJacobian(Eigen::Vector4d const& q);
Eigen::Vector4d updateQuat(Eigen::Vector4d const& q, Eigen::Vector3d const& dq);
Eigen::Matrix<double, 3, 4> quatS(Eigen::Vector4d q);
Eigen::Matrix<double, 4, 3> quatInvS(Eigen::Vector4d q);

inline Eigen::Vector3d qlog(const Eigen::Vector4d& q) { return quat2AxisAngle(q); }
inline Eigen::Vector4d qexp(const Eigen::Vector3d& theta) { return axisAngle2quat(theta); }

/// \brief do spherical linear interpolation between q0 and q1 for times t = [0.0,1.0]
Eigen::Vector4d qslerp(const Eigen::Vector4d& q0, const Eigen::Vector4d& q1, double t);

/// \brief do linear interpolation between p0 and p1 for times t = [0.0,1.0]
Eigen::VectorXd lerp(const Eigen::VectorXd& p0, const Eigen::VectorXd& p1, double t);

/// \brief Jacobian of the quat log function evaluated at p
Eigen::Matrix<double, 3, 4> quatLogJacobian(const Eigen::Vector4d& p);

/// \brief Jacobian of the quat exp function evaluated at vec
template <typename Scalar_ = double>
Eigen::Matrix<Scalar_, 4, 3> quatExpJacobian(const Eigen::Matrix<Scalar_, 3, 1>& vec);

template <typename Scalar_ = double>
Eigen::Matrix<Scalar_, 3, 4> quatLogJacobian2(const Eigen::Matrix<Scalar_, 4, 1>& p);

template <typename Scalar_ = double>
const Eigen::Matrix<Scalar_, 4, 3>& quatV();

template <typename Scalar_ = double>
Eigen::Matrix<Scalar_, 3, 3> logDiffMat(const Eigen::Matrix<Scalar_, 3, 1>& vec);

template <typename Scalar_ = double>
Eigen::Matrix<Scalar_, 3, 3> expDiffMat(const Eigen::Matrix<Scalar_, 3, 1>& vec);

extern template const Eigen::Matrix<double, 4, 3>& quatV();
extern template const Eigen::Matrix<float, 4, 3>& quatV();
extern template Eigen::Matrix<double, 4, 3> quatExpJacobian(const Eigen::Matrix<double, 3, 1>& vec);
extern template Eigen::Matrix<float, 4, 3> quatExpJacobian(const Eigen::Matrix<float, 3, 1>& vec);
extern template Eigen::Matrix<double, 3, 4> quatLogJacobian2(const Eigen::Matrix<double, 4, 1>& p);
extern template Eigen::Matrix<float, 3, 4> quatLogJacobian2(const Eigen::Matrix<float, 4, 1>& p);
extern template Eigen::Matrix<double, 3, 3> logDiffMat(const Eigen::Matrix<double, 3, 1>& vec);
extern template Eigen::Matrix<float, 3, 3> logDiffMat(const Eigen::Matrix<float, 3, 1>& vec);
extern template Eigen::Matrix<double, 3, 3> expDiffMat(const Eigen::Matrix<double, 3, 1>& vec);
extern template Eigen::Matrix<float, 3, 3> expDiffMat(const Eigen::Matrix<float, 3, 1>& vec);
}  // namespace kinematics
}  // namespace sm

#endif /* SM_QUATERNION_ALGEBRA_HPP */


#pragma once

#include <vector>
#include <cassert>

#include <iostream>
#include <iomanip>

#include <Eigen/Dense>

//! \file rkintegrator.hpp Solution for Problem 1a, implementing RkIntegrator class

//! \brief Implements a Runge-Kutta explicit solver for a given Butcher tableau for autonomous ODEs
//! \tparam State a type representing the space in which the solution lies, e.g. R^d, represented by e.g. Eigen::VectorXd.
template <class State>
class RKIntegrator {
public:
    //! \brief Constructor for the RK method.
    //! Performs size checks and copies A and b into internal storage
    //! \param[in] A matrix containing coefficents of Butcher tableau, must be (strictly) lower triangular (no check)
    //! \param[in] b vector containing coefficients of lower part of Butcher tableau
    RKIntegrator(const Eigen::MatrixXd & A, const Eigen::VectorXd & b) {
        // TODO: implement size checks and initialize internal data
    }

    //! \brief Perform the solution of the ODE
    //! Solve an autonomous ODE y' = f(y), y(0) = y0, using a RK scheme given in the Butcher tableau provided in the
    //! constructor. Performs N equidistant steps upto time T with initial data y0
    //! \tparam Function type for function implementing the rhs function. Must have State operator()(State x)
    //! \param[in] f function handle for rhs in y' = f(y), e.g. implemented using lambda funciton
    //! \param[in] T final time T
    //! \param[in] y0 initial data y(0) = y0 for y' = f(y)
    //! \param[in] N number of steps to perform. Step size is h = T / N. Steps are equidistant.
    //! \return vector containing all steps y^n (for each n) including initial and final value
    template <class Function>
    std::vector<State> solve(const Function &f, double T, const State & y0, unsigned int N) const {
        // TODO: implement solver from 0 to T, calling function step appropriately
    }

private:

    //! \brief Perform a single step of the RK method for the solution of the autonomous ODE
    //! Compute a single explicit RK step y^{n+1} = y_n + \sum ... starting from value y0 and storing next value in y1
    //! \tparam Function type for function implementing the rhs. Must have State operator()(State x)
    //! \param[in] f function handle for ths f, s.t. y' = f(y)
    //! \param[in] h step size
    //! \param[in] y0 initial state
    //! \param[out] y1 next step y^{n+1} = y^n + ...
    template <class Function>
    void step(const Function &f, double h, const State & y0, State & y1) const {
        // TODO: implement a single step of the RK method using provided Butcher scheme
    }

    //! TODO: put here suitable internal data storage
};

Nummerical Methods for CSE/PS12/solutions_ps12/rkintegrator_template.hpp

Nummerical Methods for CSE/PS12/templates_ps12/rkintegrator_template.hpp


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "matrix_gemm.h"
#include "common/pixel_benchmark.h"
#include "common/pixel_check.h"

#include <opencv2/opencv.hpp>
#include <Eigen/Dense>

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

typedef struct Matrix {
    size_t rows;
    size_t cols;
    float* data;
} Matrix;

/*
1 2     1  2    7  10
3 4     3  4    15 22
*/

static void print_matrix(float* data, uint32_t rows, uint32_t cols) {
    size_t idx=0;
    for (uint32_t i=0; i<rows; i++){
        for (uint32_t j=0; j<cols; j++) {
            printf("%.2f, ", data[idx]);
            idx++;
        }
        printf("\n");
    }
}

static void matrix_gemm_f32_opencv(float* mA, float* mB, float* mC, const uint32_t M, const uint32_t K, const uint32_t N)
{
    cv::Mat matA = cv::Mat(M, K, CV_32FC1, mA);
    cv::Mat matB = cv::Mat(K, N, CV_32FC1, mB);
    cv::Mat matC = cv::Mat(M, N, CV_32FC1, mC);
    matC = matA * matB;
    // cout << "matA:\n" << matA << endl;
    // cout << "matB:\n" << matB << endl;
    // cout << "matC:\n" << matC << endl;
}

static void matrix_gemm_f32_eigen(float* mA, float* mB, float* mC, const uint32_t M, const uint32_t K, const uint32_t N)
{
    Eigen::Map<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> eA(mA, M, K);
    Eigen::Map<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> eB(mB, K, N);
    Eigen::Map<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> eC(mC, M, N);
    eC = eA * eB;
    // cout << "eA:\n" << eA << endl;
    // cout << "eB:\n" << eB << endl;
    // cout << "eC:\n" << eC << endl;
}

static void gemm_tiny_debug()
{
    uint32_t m = 3;
    uint32_t k = 2;
    uint32_t n = 4;

    Matrix mA;
    mA.rows = m;
    mA.cols = k;

    Matrix mB;
    mB.rows = k;
    mB.cols = n;

    mA.data = (float*)malloc(mA.rows*mA.cols * sizeof(float));
    mB.data = (float*)malloc(mB.rows*mB.cols * sizeof(float));

    mA.data[0] = 2;
    mA.data[1] = -6;
    mA.data[2] = 3;
    mA.data[3] = 5;
    mA.data[4] = 1;
    mA.data[5] = -1;

    mB.data[0] = 4;
    mB.data[1] = -2;
    mB.data[2] = -4;
    mB.data[3] = -5;
    mB.data[4] = -7;
    mB.data[5] = -3;
    mB.data[6] = 6;
    mB.data[7] = 7;

    Matrix mC;
    mC.rows = m;
    mC.cols = n;
    mC.data = (float*)malloc(mC.rows*mC.cols * sizeof(float));

    matrix_gemm_f32_order_opt(mA.data, mB.data, mC.data, m, k, n);

    printf("--- matrix A:\n");
    print_matrix(mA.data, mA.rows, mA.cols);

    printf("--- matrix B:\n");
    print_matrix(mB.data, mB.rows, mB.cols);

    printf("--- matrix C:\n");
    print_matrix(mC.data, mC.rows, mC.cols);

}

static void prepare_big(uint32_t& M, uint32_t& K, uint32_t& N, cv::Mat& matA, cv::Mat& matB)
{
    cv::Mat image = cv::imread("colorhouse.png");

    cv::Size size = image.size();
    uint32_t height = size.height;
    uint32_t width = size.width;
    printf("image info: height=%u, width=%u\n", height, width);

    cv::Size transposed_size;
    transposed_size.height = size.width;
    transposed_size.width = size.height;

    std::vector<cv::Mat> channels;
    cv::split(image, channels);
    cv::Mat b_channels = channels[0];
    cv::Mat g_channels = channels[1];
    cv::Mat r_channels = channels[2];

    // matA's dim: height * width
    b_channels.convertTo(matA, CV_32FC1);

    matB = b_channels.t(); // matB's dim: width * height
    matB.convertTo(matB, CV_32FC1);

    M = height;
    K = width;
    N = height;
}

static void prepare_small(uint32_t& M, uint32_t& K, uint32_t& N, cv::Mat& matA, cv::Mat& matB)
{
    M = 2;
    K = 3;
    N = 2;

    /*
    0  1  2
    1  2  3
    */
    float* data = NULL;
    matA = cv::Mat(M, K, CV_32FC1);
    data = (float*)matA.data;
    data[0] = 1.2;
    data[1] = 2.3;
    data[2] = 3.4;
    data[3] = 4.5;
    data[4] = 5.6;
    data[5] = 6.7;

    cout << "matA:" << endl << matA << endl;

    /*
    1 2
    3 4
    5 6
    */
    matB = cv::Mat(K, N, CV_32FC1);
    data = (float*)matB.data;
    data[0] = 1.1;
    data[1] = 2.2;
    data[2] = 3.3;
    data[3] = 4.5;
    data[4] = 5.5;
    data[5] = 6.6;

    cout << "matB:" << endl << matB << endl;
}

static void gemm_f32_test() {
    //--------------------------------
    cv::Mat matA;
    cv::Mat matB;
    uint32_t M, K, N;
    prepare_big(M, K, N, matA, matB);
    //prepare_small(M, K, N, matA, matB);

    printf("-- after prepare_small:\n");
    // cout << "matA:" << endl << matA << endl;
    // cout << "matB:" << endl << matB << endl;

    float* mA = (float*)matA.data;
    float* mB = (float*)matB.data;

    size_t buf_size = M*N * sizeof(float);
    float* mC_naive = (float*)malloc(buf_size);
    float* mC_eigen = (float*)malloc(buf_size);
    float* mC_opencv = (float*)malloc(buf_size);
    float* mC_order_opt = (float*)malloc(buf_size);
    float* mC_order_opt2 = (float*)malloc(buf_size);
    float* mC_asimd = (float*)malloc(buf_size);
    for(uint32_t i=0; i<M*N; i++) {
        mC_naive[i] = 0;
        mC_eigen[i] = 0;
        mC_opencv[i] = 0;
        mC_order_opt[i] = 0;
        mC_order_opt2[i] = 0;
        mC_asimd[i] = 0;
    }

    double t_start, t_cost;

    // eigen
    t_start = pixel_get_current_time();
    matrix_gemm_f32_eigen(mA, mB, mC_eigen, M, K, N);
    t_cost = pixel_get_current_time() - t_start;
    printf("matrix gemm, eigen,     time cost %.2lf ms\n", t_cost);

    // opencv
    t_start = pixel_get_current_time();
    matrix_gemm_f32_opencv(mA, mB, mC_opencv, M, K, N);
    t_cost = pixel_get_current_time() - t_start;
    printf("matrix gemm, opencv,    time cost %.2lf ms\n", t_cost);

    // naive
    t_start = pixel_get_current_time();
    matrix_gemm_f32_naive(mA, mB, mC_naive, M, K, N);
    t_cost = pixel_get_current_time() - t_start;
    printf("matrix gemm, naive,     time cost %.2lf ms\n", t_cost);

    // order opt 调整维度顺序
    t_start = pixel_get_current_time();
    matrix_gemm_f32_order_opt(mA, mB, mC_order_opt, M, K, N);
    t_cost = pixel_get_current_time() - t_start;
    printf("matrix gemm, order opt, time cost %.2lf ms\n", t_cost);

    // order opt2 调整维度顺序2
    t_start = pixel_get_current_time();
    matrix_gemm_f32_order_opt2(mA, mB, mC_order_opt2, M, K, N);
    t_cost = pixel_get_current_time() - t_start;
    printf("matrix gemm, order opt2,time cost %.2lf ms\n", t_cost);

    // asimd
    t_start = pixel_get_current_time();
    matrix_gemm_f32_asimd(mA, mB, mC_asimd, M, K, N);
    t_cost = pixel_get_current_time() - t_start;
    printf("matrix gemm, asimd,     time cost %.2lf ms\n", t_cost);

    // validate if different implementations result match
    int mis_eigen = 0;
    int mis_opencv = 0;
    int mis_order_opt = 0;
    int mis_order_opt2 = 0;
    int mis_asimd = 0;
    uint32_t len = M * N;

    //float* mC_gt = mC_order_opt;
    float* mC_gt = mC_naive;
    float epsilon = 1e-3;
    for (uint32_t i=0; i<len; i++) {
        if (!nearly_equal_absolutely(mC_gt[i], mC_eigen[i], epsilon)) {
            mis_eigen++;
        }
        if (!nearly_equal_absolutely(mC_gt[i], mC_opencv[i], epsilon)) {
            mis_opencv++;
        }
        if (!nearly_equal_absolutely(mC_gt[i], mC_order_opt[i], epsilon)) {
            mis_order_opt++;
        }
        if (!nearly_equal_absolutely(mC_gt[i], mC_order_opt2[i], epsilon)) {
            mis_order_opt2++;
        }
        if (!nearly_equal_absolutely(mC_gt[i], mC_asimd[i], epsilon)) {
            mis_asimd++;
        }
    }

    printf("mis_eigen=%d, mis_opencv=%d, mis_order_opt=%d, mis_order_opt2=%d, mis_asimd=%d\n",
        mis_eigen, mis_opencv, mis_order_opt, mis_order_opt2, mis_asimd);

    FILE* fout = NULL;

    fout = fopen("opencv.txt", "w");
    for (uint32_t i=0; i<len; i++) {
        fprintf(fout, "%f\n", mC_opencv[i]);
    }
    fclose(fout);

    fout = fopen("eigen.txt", "w");
    for (uint32_t i=0; i<len; i++) {
        fprintf(fout, "%f\n", mC_eigen[i]);
    }
    fclose(fout);

    fout = fopen("order_opt.txt", "w");
    for (uint32_t i=0; i<len; i++) {
        fprintf(fout, "%f\n", mC_order_opt[i]);
    }
    fclose(fout);

    fout = fopen("naive.txt", "w");
    for (uint32_t i=0; i<len; i++) {
        fprintf(fout, "%f\n", mC_naive[i]);
    }
    fclose(fout);
}

int main() {
    //gemm_tiny_debug();
    gemm_f32_test();

    return 0;
}


/******************************************************************************

  This source file is part of the Avogadro project.

  Copyright (C) 2010 Eric C. Brown

  This source code is released under the New BSD License, (the "License").

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

******************************************************************************/

#include "qtaimmathutilities.h"

#include <Eigen/Eigenvalues>
#include <Eigen/QR>
#include <cmath>

namespace Avogadro {
namespace QtPlugins {
namespace QTAIMMathUtilities {

Matrix<qreal, 3, 1> eigenvaluesOfASymmetricThreeByThreeMatrix(
  const Matrix<qreal, 3, 3>& A)
{
  SelfAdjointEigenSolver<Matrix<qreal, 3, 3>> eigensolver(A);
  return eigensolver.eigenvalues();
}

Matrix<qreal, 3, 3> eigenvectorsOfASymmetricThreeByThreeMatrix(
  const Matrix<qreal, 3, 3>& A)
{
  SelfAdjointEigenSolver<Matrix<qreal, 3, 3>> eigensolver(A);
  return eigensolver.eigenvectors();
}

Matrix<qreal, 4, 1> eigenvaluesOfASymmetricFourByFourMatrix(
  const Matrix<qreal, 4, 4>& A)
{
  SelfAdjointEigenSolver<Matrix<qreal, 4, 4>> eigensolver(A);
  return eigensolver.eigenvalues();
}

Matrix<qreal, 4, 4> eigenvectorsOfASymmetricFourByFourMatrix(
  const Matrix<qreal, 4, 4>& A)
{
  SelfAdjointEigenSolver<Matrix<qreal, 4, 4>> eigensolver(A);
  return eigensolver.eigenvectors();
}

qint64 signOfARealNumber(qreal x)
{
  if (x > 0.)
    return 1;
  else if (x == 0.)
    return 0;
  else
    return -1;
}

qint64 signatureOfASymmetricThreeByThreeMatrix(const Matrix<qreal, 3, 3>& A)
{
  SelfAdjointEigenSolver<Matrix<qreal, 3, 3>> eigensolver(A);
  Matrix<qreal, 3, 1> eigenvalues = eigensolver.eigenvalues();

  return signOfARealNumber(eigenvalues(0)) + signOfARealNumber(eigenvalues(1)) +
         signOfARealNumber(eigenvalues(2));
}

qreal ellipticityOfASymmetricThreeByThreeMatrix(const Matrix<qreal, 3, 3>& A)
{
  SelfAdjointEigenSolver<Matrix<qreal, 3, 3>> eigensolver(A);
  Matrix<qreal, 3, 1> eigenvalues = eigensolver.eigenvalues();

  return (eigenvalues(0) / eigenvalues(1)) - 1.0;
}

qreal distance(const Matrix<qreal, 3, 1>& a, const Matrix<qreal, 3, 1>& b)
{
  return sqrt(pow(a(0) - b(0), 2) + pow(a(1) - b(1), 2) + pow(a(2) - b(2), 2));
}

Matrix<qreal, 3, 1> sphericalToCartesian(const Matrix<qreal, 3, 1>& rtp,
                                         const Matrix<qreal, 3, 1>& x0y0z0)
{
  qreal r = rtp(0);
  qreal theta = rtp(1);
  qreal phi = rtp(2);

  qreal x0 = x0y0z0(0);
  qreal y0 = x0y0z0(1);
  qreal z0 = x0y0z0(2);

  qreal costheta = cos(theta);
  qreal cosphi = cos(phi);
  qreal sintheta = sin(theta);
  qreal sinphi = sin(phi);

  Matrix<qreal, 3, 1> xyz(r * cosphi * sintheta + x0,
                          r * sintheta * sinphi + y0, r * costheta + z0);

  return xyz;
}

Matrix<qreal, 3, 1> sphericalToCartesian(const Matrix<qreal, 3, 1>& rtp)
{
  Matrix<qreal, 3, 1> x0y0z0(0., 0., 0.);

  return sphericalToCartesian(rtp, x0y0z0);
}

Matrix<qreal, 3, 1> cartesianToSpherical(const Matrix<qreal, 3, 1>& xyz,
                                         const Matrix<qreal, 3, 1>& x0y0z0)
{
  qreal x = xyz(0);
  qreal y = xyz(1);
  qreal z = xyz(2);

  qreal x0 = x0y0z0(0);
  qreal y0 = x0y0z0(1);
  qreal z0 = x0y0z0(2);

  qreal xshift = x - x0;
  qreal yshift = y - y0;
  qreal zshift = z - z0;

  qreal length = sqrt(pow(xshift, 2) + pow(yshift, 2) + pow(zshift, 2));

  Matrix<qreal, 3, 1> rtp;

  if (length == 0.)
    rtp << x0, y0, z0;
  else if (xshift == 0. && yshift == 0.)
    rtp << length, acos(zshift / length), 0.;
  else
    rtp << length, acos(zshift / length), atan2(xshift, yshift);

  return rtp;
}

Matrix<qreal, 3, 1> cartesianToSpherical(const Matrix<qreal, 3, 1>& xyz)
{
  Matrix<qreal, 3, 1> x0y0z0(0., 0., 0.);

  return cartesianToSpherical(xyz, x0y0z0);
}

// Cerjan-Miller-Baker-Popelier Methods
//
// Based on:
// Popelier, P.L.A. Comput. Phys. Comm. 1996, 93, 212.

Matrix<qreal, 3, 1> minusThreeSignatureLocatorGradient(
  const Matrix<qreal, 3, 1>& g, const Matrix<qreal, 3, 3>& H)
{
  Matrix<qreal, 3, 1> value;

  Matrix<qreal, 3, 1> b = eigenvaluesOfASymmetricThreeByThreeMatrix(H);
  Matrix<qreal, 3, 3> U = eigenvectorsOfASymmetricThreeByThreeMatrix(H);

  Matrix<qreal, 3, 1> F = U.transpose() * g;

  Matrix<qreal, 4, 4> A;
  A << b(0), 0., 0., F(0), 0., b(1), 0., F(1), 0., 0., b(2), F(2), F(0), F(1),
    F(2), 0.;

  Matrix<qreal, 4, 1> eval = eigenvaluesOfASymmetricFourByFourMatrix(A);

  Matrix<qreal, 3, 1> lambda;
  lambda << eval(3), eval(3), eval(3);

  Matrix<qreal, 3, 1> denom;
  denom = b - lambda;

  for (qint64 i = 0; i < 3; ++i)
    if (denom(i) < SMALL)
      denom(i) = denom(i) + SMALL;

  Matrix<qreal, 3, 1> h;
  h << 0., 0., 0.;

  for (qint64 j = 0; j < 3; ++j)
    for (qint64 i = 0; i < 3; ++i)
      h(j) = h(j) + (-F(i) * U(j, i)) / denom(i);

  value = h;

  return value;
}

Matrix<qreal, 3, 1> minusOneSignatureLocatorGradient(
  const Matrix<qreal, 3, 1>& g, const Matrix<qreal, 3, 3>& H)
{
  Matrix<qreal, 3, 1> value;

  Matrix<qreal, 3, 1> b = eigenvaluesOfASymmetricThreeByThreeMatrix(H);
  Matrix<qreal, 3, 3> U = eigenvectorsOfASymmetricThreeByThreeMatrix(H);

  Matrix<qreal, 3, 1> F = U.transpose() * g;

  Matrix<qreal, 3, 3> A;
  A << b(0), 0., F(0), 0., b(1), F(1), F(0), F(1), 0.;

  Matrix<qreal, 3, 1> eval = eigenvaluesOfASymmetricThreeByThreeMatrix(A);

  Matrix<qreal, 3, 1> lambda;
  lambda << eval(2), eval(2),
    (0.5) * (b(2) - sqrt(pow(b(2), 2) + 4.0 * pow(F(2), 2)));

  Matrix<qreal, 3, 1> denom;
  denom = b - lambda;

  for (qint64 i = 0; i < 3; ++i)
    if (denom(i) < SMALL)
      denom(i) = denom(i) + SMALL;

  Matrix<qreal, 3, 1> h;
  h << 0., 0., 0.;

  for (qint64 j = 0; j < 3; ++j)
    for (qint64 i = 0; i < 3; ++i)
      h(j) = h(j) + (-F(i) * U(j, i)) / denom(i);

  value = h;

  return value;
}

Matrix<qreal, 3, 1> plusOneSignatureLocatorGradient(
  const Matrix<qreal, 3, 1>& g, const Matrix<qreal, 3, 3>& H)
{
  Matrix<qreal, 3, 1> value;

  Matrix<qreal, 3, 1> b = eigenvaluesOfASymmetricThreeByThreeMatrix(H);
  Matrix<qreal, 3, 3> U = eigenvectorsOfASymmetricThreeByThreeMatrix(H);

  Matrix<qreal, 3, 1> F = U * g;

  Matrix<qreal, 3, 3> A;
  A << b(1), 0., F(1), 0., b(2), F(2), F(1), F(2), 0.;

  Matrix<qreal, 3, 1> eval = eigenvaluesOfASymmetricThreeByThreeMatrix(A);

  Matrix<qreal, 3, 1> lambda;
  lambda << eval(2), eval(2),
    (0.5) * (b(0) + sqrt(pow(b(0), 2) + 4.0 * pow(F(0), 2)));

  Matrix<qreal, 3, 1> denom;
  denom = b - lambda;

  for (qint64 i = 0; i < 3; ++i)
    if (denom(i) < SMALL)
      denom(i) = denom(i) + SMALL;

  Matrix<qreal, 3, 1> h;
  h << 0., 0., 0.;

  for (qint64 j = 0; j < 3; ++j)
    for (qint64 i = 0; i < 3; ++i)
      h(j) = h(j) + (-F(i) * U(i, j)) / denom(i);

  value = h;

  return value;
}

Matrix<qreal, 3, 1> plusThreeSignatureLocatorGradient(
  const Matrix<qreal, 3, 1>& g, const Matrix<qreal, 3, 3>& H)
{
  Matrix<qreal, 3, 1> value;

  Matrix<qreal, 3, 1> b = eigenvaluesOfASymmetricThreeByThreeMatrix(H);
  Matrix<qreal, 3, 3> U = eigenvectorsOfASymmetricThreeByThreeMatrix(H);

  Matrix<qreal, 3, 1> F = U * g;

  Matrix<qreal, 4, 4> A;
  A << b(0), 0., 0., F(0), 0., b(1), 0., F(1), 0., 0., b(2), F(2), F(0), F(1),
    F(2), 0.;

  Matrix<qreal, 4, 1> eval = eigenvaluesOfASymmetricFourByFourMatrix(A);

  Matrix<qreal, 3, 1> lambda;
  lambda << eval(0), eval(0), eval(0);

  Matrix<qreal, 3, 1> denom;
  denom = b - lambda;

  for (qint64 i = 0; i < 3; ++i)
    if (denom(i) < SMALL)
      denom(i) = denom(i) + SMALL;

  Matrix<qreal, 3, 1> h;
  h << 0., 0., 0.;

  for (qint64 j = 0; j < 3; ++j)
    for (qint64 i = 0; i < 3; ++i)
      h(j) = h(j) + (-F(i) * U(i, j)) / denom(i);

  value = h;

  return value;
}

} // namespace QTAIMMathUtilities
} // namespace QtPlugins
} // namespace Avogadro


#include <math.h>
#include <Eigen/Geometry>

#include "stiffness_checker/Util.h"

namespace conmech
{
namespace stiffness_checker
{

void createLocalStiffnessMatrix(const double &L, const double &A, const int &dim,
                                const double &Jx, const double &Iy, const double &Iz,
                                const double &E, const double &G, const double &mu,
                                Eigen::MatrixXd &K_eL)
{
  // TODO: add 2D case
  assert(3 == dim);

  switch(dim)
  {
    case 2:
      assert(false && "2D local stiffness not implemented.");
      return;
    case 3:
    {
      K_eL = Eigen::MatrixXd::Zero(12,12);

      // see: [Matrix Structural Analysis, McGuire et al., 2rd edition]
      // P73 - eq(4.34)
      Eigen::MatrixXd K_block(6,6);
      K_block.setZero();
      Eigen::VectorXd diag(6);

      // block_00 and block_11
      K_block(1,5) = 6*Iz / std::pow(L,2);
      K_block(2,4) = - 6*Iy / std::pow(L,2);
      K_block = K_block.eval() +  K_block.transpose().eval();
      K_eL.block<6,6>(0,0) = K_block;
      K_eL.block<6,6>(6,6) = -K_block;

      diag[0] = A/L;
      diag[1] = 12*Iz / std::pow(L,3);
      diag[2] = 12*Iy / std::pow(L,3);
      diag[3] = Jx / (2*(1+mu)*L);
      diag[4] = 4*Iy / L;
      diag[5] = 4*Iz / L;
      K_eL.block<6,6>(0,0) += Eigen::MatrixXd(diag.asDiagonal());
      K_eL.block<6,6>(6,6) += Eigen::MatrixXd(diag.asDiagonal());

      // block_01 and block_10
      K_block.setZero();

      K_block(1,5) = 6*Iz / std::pow(L,2);
      K_block(2,4) = - 6*Iy / std::pow(L,2);
      K_block = K_block.eval() - K_block.transpose().eval();
      K_eL.block<6,6>(0,6) = K_block;
      K_eL.block<6,6>(6,0) = -K_block;

      diag[0] = -A/L;
      diag[1] = -12*Iz / std::pow(L,3);
      diag[2] = -12*Iy / std::pow(L,3);
      diag[3] = -Jx / (2*(1+mu)*L);
      diag[4] = 2*Iy / L;
      diag[5] = 2*Iz / L;
      K_eL.block<6,6>(0,6) += Eigen::MatrixXd(diag.asDiagonal());
      K_eL.block<6,6>(6,0) += Eigen::MatrixXd(diag.asDiagonal());

      K_eL *= E;
    }
  }
}

/**
 * @brief Get the Global to Local Rotation Matrix object
 * Calculates a 3x3 matrix to tranform the global xyz axis to the element local axis.
 * TODO: add info on the axis convention, we have different conventions with compas_fea (abaqus)
 *
 * The coordinate transformation matrix can be used to:
 *  - transform frame element end forces from the element (local) coordinate system
 *    to the structure (global) coordinate system
 *  - transfrom end displacements from the structural (global) coordinate system
 *    to the element (local) coordinate system,
 *  - transform the frame element stiffness and mass matrices
 *    from element (local) coordinates to structral (global) coordinates.
 * Symbolically, the return matrix R = {local}_R_{global}
 *
 * @param[in] end_vert_u
 * @param[in] end_vert_v
 * @param[out] rot_m 3x3 Eigen matrix, transforming global axis to local coordinate frame
 * @param[in] rot_y2x optional rotation of local y axis around the local x axis, defaults to zero
 */
void getGlobal2LocalRotationMatrix(
    const Eigen::VectorXd & end_vert_u,
    const Eigen::VectorXd & end_vert_v,
    Eigen::Matrix3d& rot_m,
    const double& rot_y2x)
{
  assert(end_vert_u.size() == end_vert_v.size() && "vert dimension not agree!");
  assert(end_vert_u.size() == 2 || end_vert_u.size() == 3);
  int dim = end_vert_u.size();

  // length of the element
  double L = (end_vert_v - end_vert_u).norm();
  // TODO: make tol as a common shared const
  assert(L < 1e6 && "vertices too close, might be duplicated pts.");

  // by convention, the new x axis is along the element's direction
  // directional cosine of the new x axis in the global world frame
  double c_x, c_y;
  c_x = (end_vert_v[0] - end_vert_u[0]) / L;
  c_y = (end_vert_v[1] - end_vert_u[1]) / L;

  Eigen::Matrix3d R = Eigen::Matrix3d::Zero();

  if (3 == dim)
  {
    double c_z = (end_vert_v[2] - end_vert_u[2]) / L;
    auto rot_axis = Eigen::AngleAxisd(rot_y2x, Eigen::Vector3d::UnitZ());

    if (abs(c_z) == 1.0)
    {
      // the element is parallel to global z axis
      // cross product is not defined, in this case
      // it's just a rotation about the global z axis
      // in x-y plane
      R(0, 2) = -c_z;
      R(1, 1) = 1;
      R(2, 0) = c_z;
    }
    else
    {
      // local x_axis = element's vector
      auto new_x = Eigen::Vector3d(c_x, c_y, c_z);

      // local y axis = cross product with global z axis
      Eigen::Vector3d new_y = -new_x.cross(Eigen::Vector3d::UnitZ());
      new_y.normalize();

      auto new_z = new_x.cross(new_y);

      R.block<3, 1>(0, 0) = new_x;
      R.block<3, 1>(0, 1) = new_y;
      R.block<3, 1>(0, 2) = new_z;
    }
    // This is essential!
    R = R * rot_axis;
    rot_m = R.transpose();
  }
  else
  {
    // 2D rotational matrix
    R(0,0) = c_x;
    R(0,1) = c_y;
    R(1,0) = -c_y;
    R(1,1) = c_x;
    R(2,2) = 1;

    auto rot_axis = Eigen::AngleAxisd(rot_y2x, Eigen::Vector3d::UnitZ());
    assert((R - rot_axis.toRotationMatrix()).norm() > 1e-3);

    rot_m = R;
  }
}

void getNodePoints(const Eigen::MatrixXd& Vertices, const int& end_u_id, const int& end_v_id,
  Eigen::VectorXd& end_u, Eigen::VectorXd& end_v)
{
  end_u = Eigen::VectorXd(3);
  end_u << Vertices(end_u_id, 0), Vertices(end_u_id, 1), Vertices(end_u_id, 2);
  end_v = Eigen::VectorXd(3);
  end_v << Vertices(end_v_id, 0), Vertices(end_v_id, 1), Vertices(end_v_id, 2);
}

} // namespace stiffness_checker
} // namespace conmech


#include <iostream>
#include <Eigen/Dense>
#include <vector>
#include <array>
#include <string>
#include <cmath>

#include "../include/input_parser.h"
#include "../include/ply.h"
#include "../include/laminate.h"

using std::cin; using std::cout; using std::endl;
using std::string;
using std::vector;
using Eigen::Matrix; using Eigen::Matrix3d; using Eigen::Vector3d;

// Get the mid-plane strain of the laminate.
void solve_mid_strain(laminate& lam, Matrix<double, 6, 1>& load_vector);

// Get the stresses and strains for each sampling point of the laminate.
void solve_stress_strain_profile(laminate& lam, double pt_spacing);

// Construct laminate from a vector of ply, the input load, and the spacing
// between sampling points.
laminate::laminate(vector<ply>& ply_vector, Matrix<double, 6, 1>& load_vector,
                    double pt_spacing):
        ply_vector_(ply_vector), load_vector_(load_vector) {
    height_ = 0.;
    for (auto it = ply_vector_.begin(); it != ply_vector_.end(); it++) {
        height_ += it->thickness_;
    }

    A_ = Matrix3d::Zero();
    B_ = Matrix3d::Zero();
    D_ = Matrix3d::Zero();
    double bottom_coordinate = -height_/2;
    for (auto it = ply_vector_.begin(); it != ply_vector_.end(); it++) {
        double top_coordinate = bottom_coordinate + it->thickness_;
        A_ = A_ + it->Qbar_ * (top_coordinate - bottom_coordinate);
        B_ = B_ + 1./2 * it->Qbar_
            * (pow(top_coordinate, 2) - pow(bottom_coordinate, 2));
        D_ = D_ + 1./3 * it->Qbar_
            * (pow(top_coordinate, 3) - pow(bottom_coordinate, 3));

        bottom_coordinate = top_coordinate;
    }
    solve_mid_strain(*this, load_vector_);
    solve_stress_strain_profile(*this, pt_spacing);

}

void solve_mid_strain(laminate& lam, Matrix<double, 6, 1>& load_vector) {
    Matrix<double, 6, 6> stiffness = Matrix<double, 6, 6>::Zero();
    stiffness.block<3, 3>(0, 0) = lam.A_;
    stiffness.block<3, 3>(0, 3) = lam.B_;
    stiffness.block<3, 3>(3, 0) = lam.B_;
    stiffness.block<3, 3>(3, 3) = lam.D_;
    Matrix<double, 6, 1> strain_vector =
        stiffness.colPivHouseholderQr().solve(load_vector);
    lam.mid_strain_ = strain_vector.head<3>();
    lam.mid_curvature_ = strain_vector.tail<3>();
}

void solve_stress_strain_profile(laminate& lam, double pt_spacing) {

    lam.profile_pt_.push_back(-lam.height_/2);
    lam.strains_.push_back(
        lam.mid_strain_ + lam.profile_pt_.back() * lam.mid_curvature_);
    vector<ply>::size_type current_layer = 0;
    lam.stresses_.push_back(
        lam.ply_vector_[current_layer].Qbar_ * lam.strains_.back());

    double current_bottom_pt = -lam.height_/2;
    double current_top_pt = current_bottom_pt
                          + lam.ply_vector_[current_layer].thickness_;
    while (lam.profile_pt_.back() <= lam.height_/2) {
        double next_profile_pt = lam.profile_pt_.back() + pt_spacing;
        if (next_profile_pt > current_top_pt) {
            current_bottom_pt = current_top_pt;
            current_layer++;
            current_top_pt = current_bottom_pt
                           + lam.ply_vector_[current_layer].thickness_;
        }
        lam.profile_pt_.push_back(lam.profile_pt_.back() + pt_spacing);
        lam.strains_.push_back(
            lam.mid_strain_ + lam.profile_pt_.back() * lam.mid_curvature_);
        lam.stresses_.push_back(
            lam.ply_vector_[current_layer].Qbar_ * lam.strains_.back());
    }
}


/***************************************************************************
 *  @file       matrix_chain_mutiply.hpp
 *  @author     Alan.W
 *  @date       03  August 2014
 *  @remark     CLRS Algorithms implementation, using C++ templates.
 ***************************************************************************/

//!
//! ex15.2-2
//! Give a recursive algorithm MATRIX-CHAIN-MULTIPLY(A, s, i, j) that actually
//! performs the optimal matrix-chain multiplication, given the sequence of matrices
//! {A1,A2,...An}, the s table computed by MATRIX-CHAIN-ORDER , and the indices i and j .
//! (The initial call would be MATRIX-CHAIN-MULTIPLY (A, s, 1, n))
//!
//  check the lambda in function matrix_chain_multiply.
//!

#ifndef MATRIX_CHAIN_MUTIPLY_HPP
#define MATRIX_CHAIN_MUTIPLY_HPP

#include <vector>
#include <functional>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/io.hpp>
#include "matrix_chain_order.hpp"

namespace ch15 {

//! foward declarations
template<typename Range>
void
build_chain(ch15::Chain<typename Range::value_type>& chain,
            const Range& dimensions,
            const typename Range::value_type& init_val = 0);

template<typename Range>
void
build_dimensions(const ch15::Chain<typename Range::value_type>& chain,
                 Range& dimensions);

template<typename T>
void print_matrix_chain(const ch15::Chain<T>& chain);

template<typename T>
ch15::Matrix<T>
matrix_chain_multiply(const ch15::Chain<T>& chain);

/**
 * @brief build_chain
 * @param chain
 * @param dimensions
 *
 * build a matrix chain using dimemsions stored in a stl container
 */
template<typename Range>
inline void
build_chain(ch15::Chain<typename Range::value_type>& chain,
            const Range& dimensions,
            const typename Range::value_type& init_val)
{
    using ValueType =   typename Range::value_type;

    for(auto it = dimensions.begin(); it != dimensions.end() - 1; ++it)
    {
        auto mat = ch15::Matrix<ValueType>(*it, *(it + 1), init_val);
        chain.push_back(mat);
    }
}

/**
 * @brief print_matrix_chain
 * @param chain
 */
template<typename T>
inline void
print_matrix_chain(const ch15::Chain<T>& chain)
{
    for(const auto& mat : chain)
        std::cout << mat << std::endl << std::endl;
}

/**
 * @brief build_dimensions
 * @param chain
 * @param dimensions
 *
 * build dimensions from matrix chain
 */
template<typename Range>
inline void
build_dimensions(const ch15::Chain<typename Range::value_type>& chain,
                 Range& dimensions)
{
    dimensions.push_back( chain.begin()->size1() );
    for(const auto& mat : chain)
        dimensions.push_back( mat.size2());
}

/**
 * @brief matrix_chain_multiply
 * @param chain
 *
 * @complx  O(n^3)
 * for ex15.2-2
 */
template<typename T>
ch15::Matrix<T>
matrix_chain_multiply(const ch15::Chain<T>& chain)
{
    //! type def for MatrixChainOrder's parameter
    using RangeType =   std::vector<T>;
    using SizeType  =   typename ch15::Matrix<T>::size_type;

    //! build dimensions
    RangeType dimens;
    ch15::build_dimensions(chain, dimens);

    //! build optimal order
    ch15::MatrixChainOrder<RangeType> order(dimens);
    order.build();
    order.print_optimal(1,chain.size());
    std::cout << std::endl;

    //! lambda to do the real job recursively
    //! @note   ex15.2-2
    std::function<ch15::Matrix<T>(SizeType,SizeType)> multiply
            = [&](SizeType head, SizeType tail)
    {
        //! @attention below is the pseudocode for ex15.2-2
        if(head == tail)
            return chain[head - 1];
        else
            return multiply(head, order.s(head - 1,tail - 2))
                   *
                   multiply(order.s(head - 1,tail - 2) + 1, tail);
    };

    //! return the product
    return multiply(1, chain.size());
}

}//namepspace
#endif // MATRIX_CHAIN_MUTIPLY_HPP

//! test: build_chain
//!     : build_dimensions
//#include <iostream>
//#include <boost/numeric/ublas/io.hpp>
//#include "color.hpp"
//#include "matrix.hpp"
//#include "matrix_chain_mutiply.hpp"
//#include "matrix_chain_order.hpp"

//int main()
//{
//    std::vector<int> v = {30,35,15,5,10,20,25};

//    ch15::Chain<int> chain;
//    ch15::build_chain(chain, v, 2);
//    ch15::print_matrix_chain(chain);

//    std::vector<int> dimens;
//    ch15::build_dimensions(chain, dimens);
//    for(auto d : dimens)
//        std::cout << d << " ";

//    std::cout << color::red("\nend\n");
//    return 0;
//}

//! @test   matrix_chain_multiply
//!         ex15.2-2
//#include <iostream>
//#include <boost/numeric/ublas/io.hpp>
//#include "color.hpp"
//#include "matrix.hpp"
//#include "matrix_chain_mutiply.hpp"
//#include "matrix_chain_order.hpp"

//int main()
//{
//    std::vector<int> v = {30,35,15,5,10,20,25};

//    ch15::Chain<int> chain;
//    ch15::build_chain(chain, v, 2);
//    std::cout << ch15::matrix_chain_multiply(chain) << std::endl;

//    std::cout << color::red("\nend\n");
//    return 0;
//}


#pragma once

#include "BinomialCoefficient.hh"
#include <Eigen/Core>

namespace kt84 {

template <int _DimIn, int _Degree>
struct PolynomialBasisGenT {
    enum { DimOut = PolynomialBasisGenT<_DimIn, _Degree - 1>::DimOut + BinomialCoefficient<_DimIn + _Degree - 1, _Degree>::Value };
    typedef Eigen::Matrix<double, _DimIn , 1> Point;
    typedef Eigen::Matrix<double, DimOut, 1> Basis;
    typedef Eigen::Matrix<double, DimOut, _DimIn> Gradient;

    static Basis basis(const Point& x) {
        const int N = BinomialCoefficient<_DimIn + _Degree - 1, _Degree>::Value;
        Eigen::Matrix<double, N, 1> result_partial;
        int index_out = 0;
        int index_in[_Degree];
        for (int i = 0; i < _Degree; ++i)
            index_in[i] = 0;
        while (true) {
            double d = 1;
            for (int i = 0; i < _Degree; ++i)
                d *= x[index_in[i]];
            result_partial[index_out++] = d;
            int is_complete = true;
            for (int i = _Degree - 1; i >= 0; --i) {
                if (index_in[i] == _DimIn - 1)
                    continue;
                ++index_in[i];
                for (int j = i + 1; j < _Degree; ++j)
                    index_in[j] = index_in[i];
                is_complete = false;
                break;
            }
            if (is_complete)
                break;
        }
        Basis result;
        result << PolynomialBasisGenT<_DimIn, _Degree - 1>::basis(x), result_partial;
        return result;
    }
    static Gradient gradient(const Point& x) {
        const int N = BinomialCoefficient<_DimIn + _Degree - 1, _Degree>::Value;
        Eigen::Matrix<double, N, _DimIn> result_partial;
        int index_out = 0;
        int index_in[_Degree];
        for (int i = 0; i < _Degree; ++i)
            index_in[i] = 0;
        while (true) {
            for (int i = 0; i < _DimIn; ++i) {
                int cnt = 0;
                double d = 1;
                for (int j = 0; j < _Degree; ++j) {
                    if (index_in[j] == i) {
                        ++cnt;
                        continue;
                    }
                    d *= x[index_in[j]];
                }
                result_partial(index_out, i) = cnt == 0 ? 0 : cnt * d * std::pow(x[i], cnt - 1);
            }
            ++index_out;
            int is_complete = true;
            for (int i = _Degree - 1; i >= 0; --i) {
                if (index_in[i] == _DimIn - 1)
                    continue;
                ++index_in[i];
                for (int j = i + 1; j < _Degree; ++j)
                    index_in[j] = index_in[i];
                is_complete = false;
                break;
            }
            if (is_complete)
                break;
        }
        Gradient result;
        result << PolynomialBasisGenT<_DimIn, _Degree - 1>::gradient(x), result_partial;
        return result;
    }
};

template <int _DimIn>
struct PolynomialBasisGenT<_DimIn, 0> {
    enum { DimOut = 1 };
    typedef Eigen::Matrix<double, _DimIn , 1> Point;
    typedef Eigen::Matrix<double, DimOut, 1> Basis;
    typedef Eigen::Matrix<double, DimOut, _DimIn> Gradient;

    static Basis basis(const Point& x) { return Basis::Constant(1); }
    static Gradient gradient(const Point& x) { return Gradient::Zero(); }
};

}


#include "RSA.h"
#include "DataStream.h"
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/multiprecision/miller_rabin.hpp>
#include <utility>
using boost::multiprecision::cpp_int;

RSAClient::RSAClient(size_t _bit_size, int _e) {
    bit_size = _bit_size;
    e = _e;
    cpp_int p = gen_prime(bit_size, _e);
    cpp_int q = gen_prime(bit_size, _e);
    n = p * q;
    cpp_int toit = (p - 1) * (q - 1); // since (p,q) are prime
    do {
        d = inv_mod(e, toit);
        if(!d) e++;
    } while (d == 0);
}

RSA_key RSAClient::get_public_key() {
    return std::make_pair(e, n);
}

DataStream RSAClient::decrypt(const DataStream &cipher) {
    return DataStream(powm(cipher.getCppInt(), d, n), bit_size*2);
}

DataStream encrypt(const DataStream &ds, const RSA_key &key, size_t bit_size) {
    return DataStream(powm(ds.getCppInt(), key.first, key.second), bit_size*2);
}

cpp_int inv_mod(const cpp_int &num, const cpp_int &mod) {
    cpp_int r0 = mod, r1 = num;
    cpp_int t0 = 0, t1 = 1;
    while(r1 != 0) {
        cpp_int q = r0 / r1;
        cpp_int r2 = r0 - q * r1;
        cpp_int t2 = t0 - q * t1;
        r0 = r1;
        r1 = r2;
        t0 = t1;
        t1 = t2;
    }
    if(r0 > 1) {
        return 0;
    }
    return (t0 + mod) % mod;
}

cpp_int gen_prime(size_t bit_size, int no_div) {
    cpp_int prime;
    do {
        if(no_div > 0) {
            do {
                prime = get_random_key(bit_size / 8).getCppInt();
            } while ((prime - 1) % no_div == 0);
        } else {
            prime = get_random_key(bit_size / 8).getCppInt();
        }
    } while(!miller_rabin_test(prime, 100));
    return prime;
}


#include <iostream>
#include <vector>
#include <algorithm>
#include <Eigen/Dense>
#include <Eigen/Eigenvalues>
#include "powerscore.h"

void PowerScoreWorker::ComputeEigenValueCentrality(Eigen::MatrixXd A, Eigen::RowVectorXd &powerScores) {
  int dimension = A.rows();
  int idxMax;
  float epsilon = 1e-6;
  Eigen::MatrixXd T(dimension, dimension);
  Eigen::MatrixXd I(dimension, dimension);
  Eigen::MatrixXd Ones(dimension, dimension);
  Eigen::MatrixXd denominator(dimension, dimension);
  Eigen::VectorXd rowSum(dimension);
  I = Eigen::MatrixXd::Identity(dimension, dimension);
  Ones = Eigen::MatrixXd::Zero(dimension, dimension);
  Ones.setOnes(dimension, dimension);
  denominator = Eigen::MatrixXd::Zero(dimension, dimension);
  powerScores = Eigen::RowVectorXd(dimension);
  // Compute the transition matrix T from the aggression matrix A
  // Start constructing T by adding eps to non-diagonal elements of A
  T = A + epsilon * (Ones - I);
  // Compute row sums for normalization to conditional probabilities
  rowSum = T.rowwise().sum();
  denominator = rowSum.replicate(1, dimension);
  // Perform element wise division to get the transition matrix
  T = T.array() / denominator.array();
  // Compute eigenvalues and eigenvectors of the transpose of T (to get left hand eigenvalues)
  Eigen::EigenSolver<Eigen::MatrixXd> es(T.transpose());
  // Find the eigenvalue with the largest absolute value
  es.eigenvalues().array().abs().maxCoeff(&idxMax);
  // Pick the corresponding eigenvector, and compute power score
  powerScores = es.eigenvectors().transpose().row(idxMax).array().abs();
}


#include <Eigen/Dense>
#include <iostream>

using namespace Eigen;

template <class Matrix>

// The auxiliary function Atimesx computes the function A*x in a smart way, using the particular structure of the matrix A.

void Atimesx(const Matrix & d, const Matrix & a, const Matrix & x, Matrix & Ax)
{
    int n=d.size();
    Ax=(d.array()*x.array()).matrix();
    VectorXd Axcut=Ax.head(n-1);
    VectorXd acut = a.head(n-1);
    VectorXd xcut = x.head(n-1);

    Ax << Axcut + x(n-1)*acut, Ax(n-1)+ acut.transpose()*xcut;
}

// We compute A*A*x by using the function Atimesx twice with 5 dimensional random vectors.

int main(void)
{
    VectorXd a=VectorXd::Random(5);
    VectorXd d=VectorXd::Random(5);
    VectorXd x=VectorXd::Random(5);
    VectorXd Ax(5);

    Atimesx(d,a,x,Ax);
    VectorXd AAx(5);
    Atimesx(d,a,Ax,AAx);
    std::cout << "A*A*x = " << AAx << std::endl;
}

Nummerical Methods for CSE/PS1/solutions_ps1/C++/arrowmatvec2.cpp


// Copyright Paul A. Bristow 2015.

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

// Note that this file contains Quickbook mark-up as well as code
// and comments, don't change any of the special comment mark-ups!

// Example of root finding using Boost.Multiprecision.

#ifndef BOOST_MATH_STANDALONE

#include <boost/math/tools/roots.hpp>
//using boost::math::policies::policy;
//using boost::math::tools::newton_raphson_iterate;
//using boost::math::tools::halley_iterate;
//using boost::math::tools::eps_tolerance; // Binary functor for specified number of bits.
//using boost::math::tools::bracket_and_solve_root;
//using boost::math::tools::toms748_solve;

#include <boost/math/special_functions/next.hpp> // For float_distance.
#include <boost/math/special_functions/pow.hpp>
#include <boost/math/constants/constants.hpp>

//[root_finding_multiprecision_include_1
#include <boost/multiprecision/cpp_bin_float.hpp> // For cpp_bin_float_50.
#include <boost/multiprecision/cpp_dec_float.hpp> // For cpp_dec_float_50.
#ifndef _MSC_VER  // float128 is not yet supported by Microsoft compiler at 2013.
#  include <boost/multiprecision/float128.hpp> // Requires libquadmath.
#endif
//] [/root_finding_multiprecision_include_1]

#include <iostream>
// using std::cout; using std::endl;
#include <iomanip>
// using std::setw; using std::setprecision;
#include <limits>
// using std::numeric_limits;
#include <tuple>
#include <utility> // pair, make_pair

// #define BUILTIN_POW_GUESS // define to use std::pow function to obtain a guess.

template <class T>
T cbrt_2deriv(T x)
{ // return cube root of x using 1st and 2nd derivatives and Halley.
  using namespace std;  // Help ADL of std functions.
  using namespace boost::math::tools; // For halley_iterate.

  // If T is not a binary floating-point type, for example, cpp_dec_float_50
  // then frexp may not be defined,
  // so it may be necessary to compute the guess using a built-in type,
  // probably quickest using double, but perhaps with float or long double.
  // Note that the range of exponent may be restricted by a built-in-type for guess.

  typedef long double guess_type;

#ifdef BUILTIN_POW_GUESS
  guess_type pow_guess = std::pow(static_cast<guess_type>(x), static_cast<guess_type>(1) / 3);
  T guess = pow_guess;
  T min = pow_guess /2;
  T max = pow_guess * 2;
#else
  int exponent;
  frexp(static_cast<guess_type>(x), &exponent); // Get exponent of z (ignore mantissa).
  T guess = ldexp(static_cast<guess_type>(1.), exponent / 3); // Rough guess is to divide the exponent by three.
  T min = ldexp(static_cast<guess_type>(1.) / 2, exponent / 3); // Minimum possible value is half our guess.
  T max = ldexp(static_cast<guess_type>(2.), exponent / 3); // Maximum possible value is twice our guess.
#endif

  int digits = std::numeric_limits<T>::digits / 2; // Half maximum possible binary digits accuracy for type T.
  const std::uintmax_t maxit = 20;
  std::uintmax_t it = maxit;
  T result = halley_iterate(cbrt_functor_2deriv<T>(x), guess, min, max, digits, it);
  // Can show how many iterations (updated by halley_iterate).
  // std::cout << "Iterations " << it << " (from max of "<< maxit << ")." << std::endl;
  return result;
} // cbrt_2deriv(x)

template <class T>
struct cbrt_functor_2deriv
{ // Functor returning both 1st and 2nd derivatives.
  cbrt_functor_2deriv(T const& to_find_root_of) : a(to_find_root_of)
  { // Constructor stores value to find root of, for example:
  }

  // using boost::math::tuple; // to return three values.
  std::tuple<T, T, T> operator()(T const& x)
  {
    // Return both f(x) and f'(x) and f''(x).
    T fx = x*x*x - a;                     // Difference (estimate x^3 - value).
    // std::cout << "x = " << x << "\nfx = " << fx << std::endl;
    T dx = 3 * x*x;                       // 1st derivative = 3x^2.
    T d2x = 6 * x;                        // 2nd derivative = 6x.
    return std::make_tuple(fx, dx, d2x);  // 'return' fx, dx and d2x.
  }
private:
  T a;                                    // to be 'cube_rooted'.
}; // struct cbrt_functor_2deriv

template <int n, class T>
struct nth_functor_2deriv
{ // Functor returning both 1st and 2nd derivatives.

  nth_functor_2deriv(T const& to_find_root_of) : value(to_find_root_of)
  { /* Constructor stores value to find root of, for example: */ }

  // using std::tuple; // to return three values.
  std::tuple<T, T, T> operator()(T const& x)
  {
    // Return both f(x) and f'(x) and f''(x).
    using boost::math::pow;
    T fx = pow<n>(x) - value;              // Difference (estimate x^3 - value).
    T dx = n * pow<n - 1>(x);              // 1st derivative = 5x^4.
    T d2x = n * (n - 1) * pow<n - 2 >(x);  // 2nd derivative = 20 x^3
    return std::make_tuple(fx, dx, d2x);   // 'return' fx, dx and d2x.
  }
private:
  T value;                                 // to be 'nth_rooted'.
}; // struct nth_functor_2deriv

template <int n, class T>
T nth_2deriv(T x)
{
  // return nth root of x using 1st and 2nd derivatives and Halley.
  using namespace std;  // Help ADL of std functions.
  using namespace boost::math; // For halley_iterate.

  int exponent;
  frexp(x, &exponent);                                 // Get exponent of z (ignore mantissa).
  T guess = ldexp(static_cast<T>(1.), exponent / n);   // Rough guess is to divide the exponent by three.
  T min = ldexp(static_cast<T>(0.5), exponent / n);    // Minimum possible value is half our guess.
  T max = ldexp(static_cast<T>(2.), exponent / n);     // Maximum possible value is twice our guess.

  int digits = std::numeric_limits<T>::digits / 2;     // Half maximum possible binary digits accuracy for type T.
  const std::uintmax_t maxit = 50;
  std::uintmax_t it = maxit;
  T result = halley_iterate(nth_functor_2deriv<n, T>(x), guess, min, max, digits, it);
  // Can show how many iterations (updated by halley_iterate).
  std::cout << it << " iterations (from max of " << maxit << ")" << std::endl;

  return result;
} // nth_2deriv(x)

//[root_finding_multiprecision_show_1

template <typename T>
T show_cube_root(T value)
{ // Demonstrate by printing the root using all definitely significant digits.
  std::cout.precision(std::numeric_limits<T>::digits10);
  T r = cbrt_2deriv(value);
  std::cout << "value = " << value << ", cube root =" << r << std::endl;
  return r;
}

//] [/root_finding_multiprecision_show_1]

int main()
{
  std::cout << "Multiprecision Root finding Example." << std::endl;
  // Show all possibly significant decimal digits.
  std::cout.precision(std::numeric_limits<double>::digits10);
  // or use   cout.precision(max_digits10 = 2 + std::numeric_limits<double>::digits * 3010/10000);
  //[root_finding_multiprecision_example_1
  using boost::multiprecision::cpp_dec_float_50; // decimal.
  using boost::multiprecision::cpp_bin_float_50; // binary.
#ifndef _MSC_VER  // Not supported by Microsoft compiler.
  using boost::multiprecision::float128;
#endif
  //] [/root_finding_multiprecision_example_1

  try
  { // Always use try'n'catch blocks with Boost.Math to get any error messages.
    // Increase the precision to 50 decimal digits using Boost.Multiprecision
//[root_finding_multiprecision_example_2

      std::cout.precision(std::numeric_limits<cpp_dec_float_50>::digits10);

      cpp_dec_float_50 two = 2; //
      cpp_dec_float_50  r = cbrt_2deriv(two);
      std::cout << "cbrt(" << two << ") = " << r << std::endl;

      r = cbrt_2deriv(2.); // Passing a double, so ADL will compute a double precision result.
      std::cout << "cbrt(" << two << ") = " << r << std::endl;
      // cbrt(2) = 1.2599210498948731906665443602832965552806854248047 'wrong' from digits 17 onwards!
      r = cbrt_2deriv(static_cast<cpp_dec_float_50>(2.)); // Passing a cpp_dec_float_50,
      // so will compute a cpp_dec_float_50 precision result.
      std::cout << "cbrt(" << two << ") = " << r << std::endl;
      r = cbrt_2deriv<cpp_dec_float_50>(2.); // Explicitly a cpp_dec_float_50, so will compute a cpp_dec_float_50 precision result.
      std::cout << "cbrt(" << two << ") = " << r << std::endl;
      // cpp_dec_float_50 1.2599210498948731647672106072782283505702514647015
//] [/root_finding_multiprecision_example_2
     //  N[2^(1/3), 50]  1.2599210498948731647672106072782283505702514647015

      //show_cube_root(2); // Integer parameter - Errors!
      //show_cube_root(2.F); // Float parameter - Warnings!
//[root_finding_multiprecision_example_3
      show_cube_root(2.);
      show_cube_root(2.L);
      show_cube_root(two);

//] [/root_finding_multiprecision_example_3

  }
  catch (const std::exception& e)
  { // Always useful to include try&catch blocks because default policies
    // are to throw exceptions on arguments that cause errors like underflow & overflow.
    // Lacking try&catch blocks, the program will abort without a message below,
    // which may give some helpful clues as to the cause of the exception.
    std::cout <<
      "\n""Message from thrown exception was:\n   " << e.what() << std::endl;
  }
  return 0;
} // int main()

/*

Description: Autorun "J:\Cpp\MathToolkit\test\Math_test\Release\root_finding_multiprecision.exe"
Multiprecision Root finding Example.
cbrt(2) = 1.2599210498948731647672106072782283505702514647015
cbrt(2) = 1.2599210498948731906665443602832965552806854248047
cbrt(2) = 1.2599210498948731647672106072782283505702514647015
cbrt(2) = 1.2599210498948731647672106072782283505702514647015
value = 2, cube root =1.25992104989487
value = 2, cube root =1.25992104989487
value = 2, cube root =1.2599210498948731647672106072782283505702514647015

*/

#endif // BOOST_MATH_STANDALONE


/* based upon http://www.boost.org/doc/libs/1_55_0/libs/multiprecision/doc/html/boost_multiprecision/tut/floats/cpp_dec_float.html
 * Use, modification and distribution are subject to the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 * Copyright ???? 20??. */

#include <iostream>
#include <utility>

#include <boost/math/constants/constants.hpp>
#include <boost/math/special_functions/gamma.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/multiprecision/cpp_bin_float.hpp>

typedef boost::multiprecision::number<
            boost::multiprecision::backends::cpp_bin_float<
                106,
                boost::multiprecision::backends::digit_base_2,
                void,
                boost::int16_t, -1022, 1023>,
            boost::multiprecision::et_off>
        cpp_bin_float_double_double;

using boost::multiprecision::cpp_dec_float;
using boost::multiprecision::cpp_bin_float_single;
using boost::multiprecision::cpp_bin_float_double;
using boost::multiprecision::cpp_bin_float_double_extended;
using boost::multiprecision::cpp_bin_float_quad;

template <typename T>
void foo(void)
{
    std::cout << "========================" << std::endl;

    /* prints the numerical precision i.e. 64 */
    std::cout << std::numeric_limits<T>::digits10 << std::endl;

    T a = 2;
    //T b = boost::math::constants::pi<T, boost::math::policies::policy<boost::math::policies::digits2<64> > >();
    T b = boost::math::constants::pi<T>();
    T c = exp(a);
    T d = pow(c,c);
    T e = 1./c;

    std::cout << std::setprecision(std::numeric_limits<T>::max_digits10) << a << std::endl;
    std::cout << std::setprecision(std::numeric_limits<T>::max_digits10) << b << std::endl;
    std::cout << std::setprecision(std::numeric_limits<T>::max_digits10) << c << std::endl;
    std::cout << std::setprecision(std::numeric_limits<T>::max_digits10) << d << std::endl;
    std::cout << std::setprecision(std::numeric_limits<T>::max_digits10) << e << std::endl;
}

int main(void)
{
    //foo<boost::multiprecision::cpp_bin_float_single >(); // see bug.cc
    foo<boost::multiprecision::cpp_bin_float_double >();
    foo<boost::multiprecision::cpp_bin_float_double_extended >();
    foo<cpp_bin_float_double_double >();
    foo<boost::multiprecision::cpp_bin_float_quad >();
    foo<boost::multiprecision::number<cpp_dec_float<64> > >();

    return 0;
}


#include <CGAL/Simple_cartesian.h>

#include <CGAL/Surface_mesh.h>
#include <CGAL/boost/graph/graph_traits_Surface_mesh.h>

#include <CGAL/subdivision_method_3.h>
#include <CGAL/Timer.h>

#include <boost/lexical_cast.hpp>

#include <iostream>
#include <fstream>

typedef CGAL::Simple_cartesian<double>      Kernel;
typedef CGAL::Surface_mesh<Kernel::Point_3> PolygonMesh;
using namespace std;
using namespace CGAL;
namespace params = CGAL::parameters;

// ======================================================================
template <class Poly>
class WLoop_mask_3 {
  typedef Poly                                         PolygonMesh;

  typedef typename boost::graph_traits<PolygonMesh>::vertex_descriptor   vertex_descriptor;
  typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;

  typedef typename boost::property_map<PolygonMesh, vertex_point_t>::type Vertex_pmap;
  typedef typename boost::property_traits<Vertex_pmap>::value_type Point;
  typedef typename boost::property_traits<Vertex_pmap>::reference Point_ref;

  PolygonMesh& pmesh;
  Vertex_pmap vpm;

public:
  WLoop_mask_3(PolygonMesh& pmesh)
    : pmesh(pmesh), vpm(get(CGAL::vertex_point, pmesh))
  {}

  void edge_node(halfedge_descriptor hd, Point& pt) {
    Point_ref p1 = get(vpm, target(hd,pmesh));
    Point_ref p2 = get(vpm, target(opposite(hd,pmesh),pmesh));
    Point_ref f1 = get(vpm, target(next(hd,pmesh),pmesh));
    Point_ref f2 = get(vpm, target(next(opposite(hd,pmesh),pmesh),pmesh));

    pt = Point((3*(p1[0]+p2[0])+f1[0]+f2[0])/8,
               (3*(p1[1]+p2[1])+f1[1]+f2[1])/8,
               (3*(p1[2]+p2[2])+f1[2]+f2[2])/8 );
  }
  void vertex_node(vertex_descriptor vd, Point& pt) {
    double R[] = {0.0, 0.0, 0.0};
    Point_ref S = get(vpm,vd);

    std::size_t n = 0;
    for(halfedge_descriptor hd : halfedges_around_target(vd, pmesh)){
      ++n;
      Point_ref p = get(vpm, target(opposite(hd,pmesh),pmesh));
      R[0] += p[0];         R[1] += p[1];         R[2] += p[2];
    }

    if (n == 6) {
      pt = Point((10*S[0]+R[0])/16, (10*S[1]+R[1])/16, (10*S[2]+R[2])/16);
    } else if (n == 3) {
      double B = (5.0/8.0 - std::sqrt(3+2*std::cos(6.283/n))/64.0)/n;
      double A = 1-n*B;
      pt = Point((A*S[0]+B*R[0]), (A*S[1]+B*R[1]), (A*S[2]+B*R[2]));
    } else {
      double B = 3.0/8.0/n;
      double A = 1-n*B;
      pt = Point((A*S[0]+B*R[0]), (A*S[1]+B*R[1]), (A*S[2]+B*R[2]));
    }
  }

  void border_node(halfedge_descriptor hd, Point& ept, Point& vpt) {
    Point_ref ep1 = get(vpm, target(hd,pmesh));
    Point_ref ep2 = get(vpm, target(opposite(hd,pmesh),pmesh));
    ept = Point((ep1[0]+ep2[0])/2, (ep1[1]+ep2[1])/2, (ep1[2]+ep2[2])/2);

    Halfedge_around_target_circulator<Poly> vcir(hd,pmesh);
    Point_ref vp1  = get(vpm, target(opposite(*vcir,pmesh),pmesh));
    Point_ref vp0  = get(vpm, target(*vcir,pmesh));
    --vcir;
    Point_ref vp_1 = get(vpm,target(opposite(*vcir,pmesh),pmesh));
    vpt = Point((vp_1[0] + 6*vp0[0] + vp1[0])/8,
                (vp_1[1] + 6*vp0[1] + vp1[1])/8,
                (vp_1[2] + 6*vp0[2] + vp1[2])/8 );
  }
};

int main(int argc, char **argv) {
  if (argc > 4) {
    cerr << "Usage: Customized_subdivision [d] [filename_in] [filename_out] \n";
    cerr << "         d -- the depth of the subdivision (default: 1) \n";
    cerr << "         filename_in -- the input mesh (.off) (default: data/quint_tris.off) \n";
    cerr << "         filename_out -- the output mesh (.off) (default: result.off)" << endl;
    return 1;
  }

  int d = (argc > 1) ? boost::lexical_cast<int>(argv[1]) : 1;
  const std::string in_file = (argc > 2) ? argv[2] : CGAL::data_file_path("meshes/quint_tris.off");
  const char* out_file = (argc > 3) ? argv[3] : "result.off";

  PolygonMesh pmesh;
  std::ifstream in(in_file);
  if(in.fail()) {
    std::cerr << "Could not open input file " << in_file << std::endl;
    return 1;
  }
  in >> pmesh;

  Timer t;
  t.start();
  Subdivision_method_3::PTQ(pmesh, WLoop_mask_3<PolygonMesh>(pmesh), params::number_of_iterations(d));
  std::cerr << "Done (" << t.time() << " s)" << std::endl;

  std::ofstream out(out_file);
  out << pmesh;

  return 0;
}


/**
 * @file sdirk.cc
 * @brief NPDE homework SDIRK code
 * @author Unknown, Oliver Rietmann
 * @date 31.03.2021
 * @copyright Developed at ETH Zurich
 */

#include "sdirk.h"

#include <Eigen/Core>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <vector>

#include "../../../lecturecodes/helperfiles/polyfit.h"

namespace SDIRK {

/* SAM_LISTING_BEGIN_0 */
Eigen::Vector2d SdirkStep(const Eigen::Vector2d &z0, double h, double gamma) {
  Eigen::Vector2d res;
  // Compute one timestep of the SDIRK implicit RK-SSM for the linear ODE
#if SOLUTION
  // Matrix A for evaluation of f
  Eigen::Matrix2d A;
  A << 0., 1., -1., -1.;
  // Precompute and reuse factorization
  auto A_lu = (Eigen::Matrix2d::Identity() - h * gamma * A).partialPivLu();
  Eigen::Vector2d az = A * z0;

  // Increments according to \prbeqref{eq:ies}
  Eigen::Vector2d k1 = A_lu.solve(az);
  Eigen::Vector2d k2 = A_lu.solve(az + h * (1 - 2 * gamma) * A * k1);

  // Updated state
  res = z0 + h * 0.5 * (k1 + k2);
#else
  //====================
  // Your code goes here
  //====================
#endif
  return res;
}
/* SAM_LISTING_END_0 */

/* SAM_LISTING_BEGIN_1 */
std::vector<Eigen::Vector2d> SdirkSolve(const Eigen::Vector2d &z0,
                                        unsigned int M, double T,
                                        double gamma) {
  // Solution vector
  std::vector<Eigen::Vector2d> res(M + 1);
  // Solve the ODE with uniform timesteps using the SDIRK method
#if SOLUTION
  // Equidistant step size
  const double h = T / M;
  // Push initial data
  res[0] = z0;
  // Main loop
  for (unsigned int i = 1; i <= M; ++i) {
    res[i] = SdirkStep(res[i - 1], h, gamma);
  }
#else
  //====================
  // Your code goes here
  //====================
#endif
  return res;
}
/* SAM_LISTING_END_1 */

/* SAM_LISTING_BEGIN_2 */
double CvgSDIRK() {
  double conv_rate;
  // Study the convergence rate of the method.
#if SOLUTION
  // Initial data z0 = [y(0), y'(0)]
  Eigen::Vector2d z0;
  z0 << 1, 0;
  // Final time
  const double T = 10;
  // Parameter
  const double gamma = (3. + std::sqrt(3.)) / 6.;
  // Mesh sizes
  Eigen::ArrayXd err(10);
  Eigen::ArrayXd M(10);
  M << 20, 40, 80, 160, 320, 640, 1280, 2560, 5120, 10240;

  // Exact solution (only y(t)) given z0 = [y(0), y'(0)] and t
  auto yex = [&z0](double t) {
    return 1. / 3. * std::exp(-t / 2.) *
           (3. * z0(0) * std::cos(std::sqrt(3.) * t / 2.) +
            std::sqrt(3.) * z0(0) * std::sin(std::sqrt(3.) * t / 2.) +
            2. * std::sqrt(3.) * z0(1) * std::sin(std::sqrt(3.) * t / 2.));
  };

  // Store old error for rate computation
  double errold = 0;
  std::cout << std::setw(15) << "m" << std::setw(15) << "maxerr"
            << std::setw(15) << "rate" << std::endl;
  // Loop over all meshes
  for (unsigned int i = 0; i < M.size(); ++i) {
    int m = M(i);
    // Get solution
    auto sol = SdirkSolve(z0, m, T, gamma);
    // Compute error
    err(i) = std::abs(sol.back()(0) - yex(T));

    // Print table
    std::cout << std::setw(15) << m << std::setw(15) << err(i);
    if (i > 0) std::cout << std::setw(15) << std::log2(errold / err(i));
    std::cout << std::endl;

    // Store old error
    errold = err(i);
  }

  Eigen::VectorXd coeffs = polyfit(M.log(), err.log(), 1);
  conv_rate = -coeffs(0);
#else
  //====================
  // Your code goes here
  //====================
#endif
  return conv_rate;
}
/* SAM_LISTING_END_2 */

}  // namespace SDIRK


/*!
 * \file PlainGeometry.hpp
 * \author Jun Yoshida
 * \copyright (c) 2019 Jun Yoshida.
 * The project is released under the MIT License.
 * \date February 20, 2020: created
 */

#pragma once

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

namespace {
enum Orientation {
    OnLine,
    Clockwise,
    CntrClockwise
};

Orientation orientation(
    Eigen::Vector2d const& p0,
    Eigen::Vector2d const& p1,
    Eigen::Vector2d const& p2) noexcept;

//! Compute the convex hull.
//! \param A set of vertices.
//! \return A list of control points spanning the hull. They are stored in the counter-clockwise order.
std::vector<Eigen::Vector2d> getConvexHull(
    std::vector<Eigen::Vector2d> const& vs) noexcept;

//! Check if the convex hull of control points overlaps with that of another Bezier curve.
//! Here, so-called *The Separating Axis Theorem* is used.
bool hasHullIntersection(
    std::vector<Eigen::Vector2d> const& lhs,
    std::vector<Eigen::Vector2d> const& rhs) noexcept;

}


///////////////////////////////////////////////////////////////////
//  Copyright Eduardo Quintana 2021
//  Copyright Janek Kozicki 2021
//  Copyright Christopher Kormanyos 2021
//  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)
/*
  boost::math::fft Example for non-complex types
  Use of DFT for Number Theoretical Transform.
*/
#include <boost/math/fft/bsl_backend.hpp>
namespace fft = boost::math::fft;

#include <iostream>
#include <vector>
#include "fft_test_helpers.hpp"

class Z337
{
public:
  typedef int integer;
  static constexpr integer mod{337}; // 337 = 2*2*2*2*3*7 + 1
};

int convolution()
/*
  product of two integer by means of the NTT,
  using the convolution theorem
*/
{
  int errors = 0;
  using M_int = fft::my_modulo_lib::mint<Z337>;
  // 85 is a primitive root of 337,
  // ie. the smallest number k for such that 85^k mod 337 = 1 is k=phi(337)=336
  const M_int w{85};
  const M_int inv_8{M_int{8}.inverse()};

  // Multiplying 1234 times 5678 = 7006652
  std::vector<M_int> A{4, 3, 2, 1, 0, 0, 0, 0};
  std::vector<M_int> B{8, 7, 6, 5, 0, 0, 0, 0};

  // forward FFT
  fft::bsl_algebraic_transform::forward(A.cbegin(),A.cend(),A.begin(), w);
  fft::bsl_algebraic_transform::forward(B.cbegin(),B.cend(),B.begin(), w);

  // convolution in Fourier space
  std::vector<M_int> AB;
  std::transform(A.begin(), A.end(), B.begin(),
                 std::back_inserter(AB),
                 [](M_int x, M_int y) { return x * y; });

  // backwards FFT
  fft::bsl_algebraic_transform::backward(AB.cbegin(),AB.cend(),AB.begin(),w);
  std::transform(AB.begin(), AB.end(), AB.begin(),
                 [&inv_8](M_int x) { return x * inv_8; });

  // carry the remainders in base 10
  std::vector<int> C;
  M_int r{0};
  for (auto x : AB)
  {
    auto y = x + r;
    C.emplace_back(int(y) % 10);
    r = M_int(int(y) / 10);
  }
  // yields 7006652
  if(static_cast<int>(C.size())!=8) errors++;
  if(C[0]!=2) errors++;
  if(C[1]!=5) errors++;
  if(C[2]!=6) errors++;
  if(C[3]!=6) errors++;
  if(C[4]!=0) errors++;
  if(C[5]!=0) errors++;
  if(C[6]!=7) errors++;
  if(C[7]!=0) errors++;
  return errors;
}
int main()
{
  return convolution();
}


#include <iostream>
#include <Eigen/Dense>

using namespace Eigen;
using namespace std;

extern "C" void dggev_(const char* JOBVL, const char* JOBVR, const int* N,
        const double* A, const int* LDA, const double* B, const int* LDB,
        double* ALPHAR, double* ALPHAI, double* BETA,
        double* VL, const int* LDVL, double* VR, const int* LDVR,
        double* WORK, const int* LWORK, int* INFO);

// Generalised Eigen-Problem
// source adapted from https://eigen.tuxfamily.org/index.php?title=Lapack
// Solve:
// A * v(j) = lambda(j) * B * v(j).
//
// v are the eigenvectors and are stored in v.
// lambda are the eigenvalues and are stored in lambda.
// The eigenvalues are stored as: (lambda(:, 1) + lambda(:, 2)*i)./lambda(:, 3)
//
// returns true on success.
// A and B will be changed.
bool GEP(MatrixXd& A, MatrixXd& B, MatrixXd& v, MatrixXd& lambda)
{
    int N = A.cols(); // Number of columns of A and B. Number of rows of v.
    if (B.cols() != N  || A.rows()!=N || B.rows()!=N){
        cout << "Matrices A and B are not square and the same size, line " << __LINE__ <<  endl;
        return false;
    }

    v.resize(N,N);
    lambda.resize(N, 3);

    int LDA = A.outerStride();
    int LDB = B.outerStride();
    int LDV = v.outerStride();

    double WORKDUMMY;
    int LWORK = -1; // Request optimum work size.
    int INFO = 0;

//    double * alphar = const_cast<double*>(lambda.col(0).data());
//    double * alphai = const_cast<double*>(lambda.col(1).data());
//    double * beta   = const_cast<double*>(lambda.col(2).data());

    double * alphar = lambda.col(0).data();
    double * alphai = lambda.col(1).data();
    double * beta   = lambda.col(2).data();

    // Get the optimum work size.
    dggev_("N", "V", &N, A.data(), &LDA, B.data(), &LDB, alphar, alphai, beta, 0, &LDV, v.data(), &LDV, &WORKDUMMY, &LWORK, &INFO);

    LWORK = int(WORKDUMMY) + 32;
    VectorXd WORK(LWORK);

    dggev_("N", "V", &N, A.data(), &LDA, B.data(), &LDB, alphar, alphai, beta, 0, &LDV, v.data(), &LDV, WORK.data(), &LWORK, &INFO);

    return INFO==0;
}

int main(){

    MatrixXd A;
    MatrixXd B;
    MatrixXd v;
    MatrixXd lambda;

    A.setRandom(4, 4);
    B.setRandom(4, 4);

    //A(1, 1) = 2;

    cout << "Before calling the GEP function. " << endl;
    cout << "Matrix A " << endl << A << endl;
    cout << "Matrix B " << endl << B << endl;

    bool success = GEP(A, B, v, lambda);

    if (success){
        cout << "A " << endl << A << endl;
        cout << "B" << endl << B << endl;

        cout << "lambda " << endl << lambda << endl;
        cout << "v " << endl << v << endl;

    }   else {
        cout << "GEP failed." << endl;
    }

    return 0;
}


#include "kabsch.h"
#include <Eigen/Dense>

using namespace probreg;

KabschResult probreg::computeKabsch(const MatrixX3& model,
                                    const MatrixX3& target,
                                    const Vector& weight) {
    //Compute the center
    Vector3 model_center = Vector3::Zero();
    Vector3 target_center = Vector3::Zero();
    Float total_weight = 0.0f;
    for(auto i = 0; i < model.rows(); ++i) {
        const Float w_i = weight[i];
        total_weight += w_i;
        model_center.noalias() += w_i * model.row(i);
        target_center.noalias() += w_i * target.row(i);
    }
    if (total_weight == 0) {
        return std::make_pair(Matrix3::Identity(), Vector3::Zero());
    }
    const Float divided_by = 1.0f / total_weight;
    model_center *= divided_by;
    target_center *= divided_by;

    //Centralize them
    //Compute the H matrix
    Float h_weight = 0.0f;
    Matrix3 hh = Matrix3::Zero();
    for(auto k = 0; k < model.rows(); ++k) {
        const auto& model_k = model.row(k).transpose();
        auto centralized_model_k = model_k - model_center;
        const auto& target_k = target.row(k).transpose();
        auto centralized_target_k = target_k - target_center;
        const Float this_weight = weight[k];
        h_weight += this_weight * this_weight;
        hh.noalias() += (this_weight * this_weight) * centralized_model_k * centralized_target_k.transpose();
    }

    //Do svd
    hh /= h_weight;
    Eigen::JacobiSVD<Matrix3> svd(hh, Eigen::ComputeFullU | Eigen::ComputeFullV);
    Vector3 ss = Vector3::Ones(3);
    ss[2] = (svd.matrixU() * svd.matrixV()).determinant();
    const Matrix3 r = svd.matrixV() * ss.asDiagonal() * svd.matrixU().transpose();

    //The translation
    Vector3 translation = target_center;
    translation.noalias() -= r * model_center;

    return std::make_pair(r, translation);
}


// Software License for MTL
//
// Copyright (c) 2007 The Trustees of Indiana University.
//               2008 Dresden University of Technology and the Trustees of Indiana University.
//               2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also license.mtl.txt in the distribution.

#include <iostream>
#include <boost/numeric/mtl/mtl.hpp>

using namespace std;

// Alleged error reported by Dragan Vidovic

template<class ct>
mtl::dense2D<ct> inv2 ( const mtl::dense2D<ct> & M )
{
   mtl::dense2D<ct> N(2,2);
   ct d = M[0][0]*M[1][1]-M[0][1]*M[1][0];
   N[0][0] = M[1][1]/d;
   N[0][1] =-M[0][1]/d;
   N[1][0] =-M[1][0]/d;
   N[1][1] = M[0][0]/d; return N;
}

int main(int, char**)
{
    typedef double ct;
    mtl::dense2D<ct> tmp(2, 2);
    tmp= 3, 5,
	 8, 9;

    mtl::dense2D<ct> tmp1 = inv2(tmp), P(tmp * tmp1);
    cout << "tmp1 is\n" << tmp1 << "\ntmp * tmp1 is:\n" << P;

    return 0;
}


#ifndef _KERNEL_HPP_
#define _KERNEL_HPP_

#include <Eigen/Core>
#include <Eigen/Eigenvalues>
#include <Eigen/SparseCore>
#include <cstdarg>
#include <mimkl/definitions.hpp>

using mimkl::definitions::Index;

namespace mimkl
{
namespace kernel
{

//! The linear kernel with the extended kernel K = X*Y_t
/*!
\param lhs left hand side NxM data-matrix.
\param rhs untransposed (unconjugated) right hand side KxM data-matrix.
\returns kernel_matrix the similarity NxK matrix.
\sa test/linear_induction
*/
template <typename KDerived, typename LhsDerived, typename RhsDerived>
KDerived linear_kernel(LhsDerived &lhs, RhsDerived &rhs)
{
    return lhs * rhs.adjoint();
}

//! The polynomial kernel with the extended kernel K =
//! (X*Y_t + c)^p
/*!
\param lhs left hand side NxM data-matrix.
\param rhs untransposed (unconjugated)  right hand side KxM data-matrix.
\param degree polynomial degree.
\param offset "free parameter trading off the influence of higher-order versus
lower-order terms in the polynomial".
\returns kernel_matrix the similarity NxK matrix.
\sa TODO
*/
template <typename KDerived, typename LhsDerived, typename RhsDerived>
KDerived polynomial_kernel(LhsDerived &lhs,
                           RhsDerived &rhs,
                           const double degree,
                           const double offset)
{
    return (linear_kernel<KDerived>(lhs, rhs).array() + offset).pow(degree).matrix();
}

//! The gaussian kernel  k = exp(-
//! (x-y)_t*(x-y) / ( 2*s^2 )).
/*!  This squared pairwise euclidean distance cannot be expressed in a concise
matrix multiplication.
The squared distance of xi to yj = \f$(x_i^T*x_i) -(x_i^T*y_j)
-(y_j^T*x_i) + (y_j^T*y_j) \f$
This means next to \f$XY^T\f$ (the linear kernel) only the diagonal entries of
\f$XX^T\f$ and \f$YY^T\f$ are needed.
\param lhs left hand side NxM data-matrix.
\param rhs untransposed  (unconjugated) right hand side KxM data-matrix.
\param sigma_square variance of the bell curve.
\returns kernel_matrix the similarity NxK matrix.
\sa gaussian_induction
*/
template <typename KDerived, typename LhsDerived, typename RhsDerived>
KDerived
gaussian_kernel(LhsDerived &lhs, RhsDerived &rhs, const double sigma_square)
{

    return (-(
            /*! -2* lhs_inducer_rhs only in case of scalar matrices.
             with complex numbers we need: -lhs_rhs
             -lhs_rhs.adjoint
             imaginary parts cancel out! */
            ((-2 * (lhs * rhs.adjoint()).real()).colwise() +
             lhs.rowwise().squaredNorm())
            .rowwise() +
            lhs.rowwise().squaredNorm().transpose()) /
            (2 * sigma_square))
    .array()
    .exp()
    .matrix();
}

//! The sigmoidal kernel with the extended kernel k = tanh(a*
//! (x_t*y) +b).
/*!
\param lhs left hand side NxM data-matrix.
\param rhs untransposed  (unconjugated) right hand side KxM data-matrix.
\param a
\param b
\returns kernel_matrix the similarity NxK matrix.
\sa test/sigmoidal_induction
*/
template <typename KDerived, typename LhsDerived, typename RhsDerived>
KDerived
sigmoidal_kernel(LhsDerived &lhs, RhsDerived &rhs, const double a, const double b)
{

    return (a * linear_kernel<KDerived>(lhs, rhs).array() + b).tanh().matrix();
}

} // namespace kernel
} // namespace mimkl

#endif /*_KERNEL_HPP_*/


/* Copyright 2017 The sfcpp Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#pragma once

#include <Eigen/Dense>

#include <iostream>

namespace sfcpp {
namespace math {

class VectorSubspace {
  Eigen::MatrixXd baseMat, kernelMat;
  size_t numCoordinates;
  static constexpr double myeps = 1e-8;
  static constexpr double mysqeps = myeps * myeps;

 public:
  VectorSubspace(size_t numCoordinates)
      : baseMat(Eigen::MatrixXd::Zero(numCoordinates, 1)),
        kernelMat(Eigen::MatrixXd::Identity(numCoordinates, numCoordinates)),
        numCoordinates(numCoordinates) {}

  void addVector(Eigen::VectorXd const &vec) {
    if (!containsVector(vec)) {
      addIndependentVector(vec);
    }
  }

  void addIndependentVector(Eigen::VectorXd const &vec) {
    baseMat.conservativeResize(baseMat.rows(), baseMat.cols() + 1);
    baseMat.col(baseMat.cols() - 1) = vec;

    kernelMat = baseMat.transpose().fullPivLu().kernel();
  }

  bool containsVector(Eigen::VectorXd const &vec) const {
    return (vec.transpose() * kernelMat).squaredNorm() <= mysqeps;
  }

  bool isOrthogonalTo(Eigen::VectorXd const &vec) const {
    return (vec.transpose() * baseMat).squaredNorm() <= mysqeps;
  }

  /**
   * @return a vector orthogonal to the subspace, which is nonzero iff the
   * subspace is not the whole space.
   */
  Eigen::VectorXd orthogonalVector() { return kernelMat.col(0); }
};

} /* namespace math */
} /* namespace sfcpp */


#pragma once
#include "coordinate_transform.hpp"
#include "integrate.hpp"
#include "shape.hpp"
#include <Eigen/Core>
#include <Eigen/Dense>
#include <functional>

//! Computes the L^2 differences between
//! u1 (considered as coefficients for the shape functions)
//! and u2.
double computeL2Difference(const Eigen::MatrixXd &vertices,
                           const Eigen::MatrixXi &triangles,
                           const Eigen::VectorXd &u1,
                           const std::function<double(double, double)> &u2) {
	const int numberOfElements = triangles.rows();

	double error = 0;
	for (int i = 0; i < numberOfElements; ++i) {
		auto &indexSet = triangles.row(i);

		const int i0 = indexSet(0);
		const int i1 = indexSet(1);
		const int i2 = indexSet(2);

		const auto &a = vertices.row(i0);
		const auto &b = vertices.row(i1);
		const auto &c = vertices.row(i2);

		auto coordinateTransform = makeCoordinateTransform(b - a, c - a);
		auto volumeFactor        = std::abs(coordinateTransform.determinant());

		error += integrate([&](double x, double y) {
			Eigen::Vector2d z = coordinateTransform * Eigen::Vector2d(x, y) + Eigen::Vector2d(a(0), a(1));

			double approximateValue = u1(i0) * lambda(0, x, y) + u1(i1) * lambda(1, x, y) + u1(i2) * lambda(2, x, y);

			return std::pow(std::abs(u2(z(0), z(1)) - approximateValue), 2) * volumeFactor;
		});
	}

	return std::sqrt(error);
}


#include <random>
#include <algorithm>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics.hpp>
#include <Polynomial.hh>

double discount(const double risk_free_rate, const int days){
    return std::exp(-risk_free_rate * (double(days) / 252.0));
}

void monte_carlo(std::vector<double> &data, std::vector<std::vector<double> > &sim_vec, int sim_len, const int iterations){
    double sigma;
    double mu;
    double drift;
    std::vector<double> log_returns;
    std::vector<double> sim;
    log_returns.reserve(data.size()-1);
    sim.reserve(sim_len);

    for(int i=1; i!=data.size(); ++i){
        log_returns.emplace_back(log(data[i] / data[i-1]));
    }

    // get standard deviation and mean
    using namespace boost::accumulators;
    accumulator_set<double, features<tag::mean, tag::variance>> acc;

    for(std::vector<double>::iterator it=log_returns.begin(); it!=log_returns.end(); ++it){
        acc(*it);
    }

    sigma = std::sqrt(variance(acc));
    mu = mean(acc);
    drift = mu - (0.5 * pow(sigma, 2.0));

    std::random_device rand;
    std::mt19937_64 gen(rand());
    std::normal_distribution<double> dist(0.0, sigma);

    // generate simulation
    for(int i=0; i!=iterations; ++i){
        sim.emplace_back(data[data.size()-1] * exp(drift + dist(gen)));
        for(int x=1; x!=sim_len; ++x){
            sim.emplace_back(sim.back() * exp(drift + dist(gen)));
        }
        sim_vec.emplace_back(sim);
        sim.clear();
    }
}

double monte_carlo_fixed_strike_arithmatic_avg_asian_call(std::vector<double> &data_underlying, const double strike, const double risk_free_rate, const int days_to_exp, const int iterations){
    std::vector<double> average_prices;
    std::vector<double> payout;
    std::vector<std::vector<double> > vec;
    double sum;

    // perform Monte Carlo on underlying asset
    monte_carlo(data_underlying, vec, days_to_exp, iterations);

    average_prices.reserve(iterations);
    payout.reserve(iterations);

    for(int i=0; i!=vec.size(); ++i){
        sum = 0.0;
        for(std::vector<double>::iterator it=vec[i].begin(); it!=vec[i].end(); ++it){
            sum += *it;
        }
        average_prices.emplace_back(sum / double(days_to_exp));
    }

    // determine average payout & discount back
    sum = 0.0;
    for(std::vector<double>::iterator it=average_prices.begin(); it!=average_prices.end(); ++it){
        sum += std::max(*it - strike, 0.0);
    }
    return (sum / double(iterations)) * std::exp(-risk_free_rate * (double(days_to_exp) / 252.0));
}

double monte_carlo_fixed_strike_arithmatic_avg_asian_put(std::vector<double> &data_underlying, const double strike, const double risk_free_rate, const int days_to_exp, const int iterations){
    std::vector<double> average_prices;
    std::vector<double> payout;
    std::vector<std::vector<double> > vec;
    double sum;

    // perform Monte Carlo on underlying asset
    monte_carlo(data_underlying, vec, days_to_exp, iterations);

    average_prices.reserve(iterations);
    payout.reserve(iterations);

    for(int i=0; i!=vec.size(); ++i){
        sum = 0.0;
        for(std::vector<double>::iterator it=vec[i].begin(); it!=vec[i].end(); ++it){
            sum += *it;
        }
        average_prices.emplace_back(sum / double(days_to_exp));
    }

    // determine average payout & discount back
    sum = 0.0;
    for(std::vector<double>::iterator it=average_prices.begin(); it!=average_prices.end(); ++it){
        sum += std::max(strike - *it, 0.0);
    }
    return (sum / double(iterations)) * std::exp(-risk_free_rate * (double(days_to_exp) / 252.0));
}

double monte_carlo_floating_strike_arithmatic_avg_asian_call(std::vector<double> &data_underlying, const double strike, const double risk_free_rate, const int days_to_exp, const int iterations){
    std::vector<double> average_prices;
    std::vector<double> payout;
    double maturity_price;
    std::vector<std::vector<double> > vec;
    double sum;

    // perform Monte Carlo on underlying asset
    monte_carlo(data_underlying, vec, days_to_exp, iterations);

    average_prices.reserve(iterations);
    payout.reserve(iterations);

    for(int i=0; i!=vec.size(); ++i){
        sum = 0.0;
        for(std::vector<double>::iterator it=vec[i].begin(); it!=vec[i].end(); ++it){
            sum += *it;
        }
        average_prices.emplace_back(sum / double(days_to_exp));
    }

    // get average price at maturity
    sum = 0.0;
    for(int i=0; i!=vec.size(); ++i){
        sum += vec[i].back();
    }
    maturity_price = sum / double(iterations);

    // determine average payout & discount back
    sum = 0.0;
    for(std::vector<double>::iterator it=average_prices.begin(); it!=average_prices.end(); ++it){
        sum += std::max(maturity_price - (*it * strike), 0.0);
    }
    return (sum / double(iterations)) * std::exp(-risk_free_rate * (double(days_to_exp) / 252.0));
}

double monte_carlo_floating_strike_arithmatic_avg_asian_put(std::vector<double> &data_underlying, const double strike, const double risk_free_rate, const int days_to_exp, const int iterations){
    std::vector<double> average_prices;
    std::vector<double> payout;
    double maturity_price;
    std::vector<std::vector<double> > vec;
    double sum;

    // perform Monte Carlo on underlying asset
    monte_carlo(data_underlying, vec, days_to_exp, iterations);

    average_prices.reserve(iterations);
    payout.reserve(iterations);

    for(int i=0; i!=vec.size(); ++i){
        sum = 0.0;
        for(std::vector<double>::iterator it=vec[i].begin(); it!=vec[i].end(); ++it){
            sum += *it;
        }
        average_prices.emplace_back(sum / double(days_to_exp));
    }

    // get average price at maturity
    sum = 0.0;
    for(int i=0; i!=vec.size(); ++i){
        sum += vec[i].back();
    }
    maturity_price = sum / double(iterations);

    // determine average payout & discount back
    sum = 0.0;
    for(std::vector<double>::iterator it=average_prices.begin(); it!=average_prices.end(); ++it){
        sum += std::max((*it * strike) - maturity_price, 0.0);
    }
    return (sum / double(iterations)) * std::exp(-risk_free_rate * (double(days_to_exp) / 252.0));
}

double american_put_longstaff_schwartz(std::vector<double> &data_underlying, const double strike, const double risk_free_rate, const int days_to_exp, const int iterations){
    std::vector<double> price;
    double iter_cont;  // continuation value for this particular iteration
    std::vector<std::vector<double> > sim_vec;
    std::vector<std::vector<double> > cfm; // cash flow matrix
    std::vector<double> day_cash; // cash flow for one day
    day_cash.reserve(iterations);

    double one_day_discount = discount(risk_free_rate, 1);

    // run Monte Carlo on underlying asset
    monte_carlo(data_underlying, sim_vec, days_to_exp, iterations);

    // work backwards, starting with maturity date
    for(std::vector<std::vector<double>>::iterator it=sim_vec.begin(); it!=sim_vec.end(); ++it){
        day_cash.emplace_back(std::max(it->back() - strike, 0.0));
    }
    cfm.emplace_back(day_cash);

    for(int i=days_to_exp-2; i!=-1; --i){
        // iterate backward
        for(int x=0; x!=iterations; ++x){
            day_cash[x] = std::max(sim_vec[x][i] - strike, 0.0);
        }

        std::vector<double> xs;
        std::vector<double> ys;
        std::vector<double> continuation;
        xs.reserve(iterations);
        ys.reserve(iterations);
        continuation.reserve(iterations);

        for(int x=0; x!=iterations; ++x){
            if(day_cash[x] > 0.0){
                xs.emplace_back(sim_vec[x][i]);
                ys.emplace_back(cfm[0][x] * one_day_discount);
            }
        }

        // get 2nd order polynomial
        Polynomial poly(xs, ys, 1, 2);

        // get continuation value
        for(int x=0; x!=iterations; ++x){
            if(day_cash[x] > 0.0){
                // option is in the money
                price = {sim_vec[x][i]};
                iter_cont = poly.eval(price) * one_day_discount;
                if(iter_cont > day_cash[x]){
                    // continuation value is greater than cash flow from exercising the option today
                    // set today's cash flow to zero
                    day_cash[x] = 0.0;
                }
                else{
                    // exercise option today!
                    // set future cash flows to zero
                    for(int q=0; q!=cfm.size(); ++q){
                        cfm[q][x] = 0.0;
                    }
                }
            }
            else{
                // option is out of the money, don't exercise today
                // set today's cash flow to zero
                day_cash[x] = 0.0;
            }
        }

        // exercise data to cash flow matrix
        std::vector<std::vector<double>>::iterator insert_front = cfm.begin();
        cfm.insert(insert_front, day_cash);
    }

    double sum = 0.0;
    int count = 0;
    for(int i=0; i!=iterations; ++i){
        for(int x=0; x!=days_to_exp; ++x){
            if(cfm[x][i] > 0.0){
                sum += cfm[x][i] * discount(risk_free_rate, x+1);
                ++count;
                break;
            }
        }
    }
    // average discounted cash flows
    return sum/double(count);
}

double american_call_longstaff_schwartz(std::vector<double> &data_underlying, const double strike, const double risk_free_rate, const int days_to_exp, const int iterations){
    std::vector<double> price;
    double iter_cont;  // continuation value for this particular iteration
    std::vector<std::vector<double> > sim_vec;
    std::vector<std::vector<double> > cfm; // cash flow matrix
    std::vector<double> day_cash; // cash flow for one day
    day_cash.reserve(iterations);

    double one_day_discount = discount(risk_free_rate, 1);

    // run Monte Carlo on underlying asset
    monte_carlo(data_underlying, sim_vec, days_to_exp, iterations);

    // work backwards, starting with maturity date
    for(std::vector<std::vector<double>>::iterator it=sim_vec.begin(); it!=sim_vec.end(); ++it){
        day_cash.emplace_back(std::max(strike - it->back(), 0.0));
    }
    cfm.emplace_back(day_cash);

    for(int i=days_to_exp-2; i!=-1; --i){
        // iterate backward
        for(int x=0; x!=iterations; ++x){
            day_cash[x] = std::max(strike - sim_vec[x][i], 0.0);
        }

        std::vector<double> xs;
        std::vector<double> ys;
        std::vector<double> continuation;
        xs.reserve(iterations);
        ys.reserve(iterations);
        continuation.reserve(iterations);

        for(int x=0; x!=iterations; ++x){
            if(day_cash[x] > 0.0){
                xs.emplace_back(sim_vec[x][i]);
                ys.emplace_back(cfm[0][x] * one_day_discount);
            }
        }

        // get 2nd order polynomial
        Polynomial poly(xs, ys, 1, 2);

        // get continuation value
        for(int x=0; x!=iterations; ++x){
            if(day_cash[x] > 0.0){
                // option is in the money
                price = {sim_vec[x][i]};
                iter_cont = poly.eval(price) * one_day_discount;
                if(iter_cont > day_cash[x]){
                    // continuation value is greater than cash flow from exercising the option today
                    // set today's cash flow to zero
                    day_cash[x] = 0.0;
                }
                else{
                    // exercise option today!
                    // set future cash flows to zero
                    for(int q=0; q!=cfm.size(); ++q){
                        cfm[q][x] = 0.0;
                    }
                }
            }
            else{
                // option is out of the money, don't exercise today
                // set today's cash flow to zero
                day_cash[x] = 0.0;
            }
        }

        // exercise data to cash flow matrix
        std::vector<std::vector<double>>::iterator insert_front = cfm.begin();
        cfm.insert(insert_front, day_cash);
    }

    double sum = 0.0;
    int count = 0;
    for(int i=0; i!=iterations; ++i){
        for(int x=0; x!=days_to_exp; ++x){
            if(cfm[x][i] > 0.0){
                sum += cfm[x][i] * discount(risk_free_rate, x+1);
                ++count;
                break;
            }
        }
    }
    // average discounted cash flows
    return sum/double(count);
}


#include <Eigen/Dense>
#include "../include/loss.h"

namespace MyDL
{

    double cross_entropy_error(MatrixXd &y, MatrixXd &t)
    {
        int batch_size = y.rows();
        double loss = -(t.array() * y.array().log()).sum() / batch_size;
        return loss;
    }

}