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

#pragma once

//libraries
#include <Eigen/Dense>
#include <unsupported/Eigen/CXX11/Tensor>

//local
#include "typedefs.hpp"

namespace math {

/**
 * Locates the maximum L2 norm (length) of the vector in the given field.
 * @param[out] max_norm length of the longest vector
 * @param[out] coordinate the location of the longest vector
 * @param[in] vector_field the vector field to look at
 */
template<typename Scalar>
void locate_max_norm(typename Scalar::Scalar& max_norm, math::Vector2i& coordinates,
		const Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>& vector_field);
/**
 * Locates the maximum L2 norm (length) of the vector in the given field.
 * @overload
 */
template<typename Scalar>
void locate_max_norm(typename Scalar::Scalar& max_norm, math::Vector3i& coordinates,
		const Eigen::Tensor<Scalar, 3, Eigen::ColMajor>& vector_field);
/**
 * Locates the minimum L2 norm (length) of the vector in the given field.
 * @param[out] max_norm length of the longest vector
 * @param[out] coordinate the location of the longest vector
 * @param[in] vector_field the vector field to look at
 */
template<typename Scalar>
void locate_min_norm(typename Scalar::Scalar& min_norm, math::Vector2i& coordinates,
		const Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>& vector_field);

/**
 * Locates the minimum L2 norm (length) of the vector in the given field.
 * @overload
 */
template<typename Scalar>
void locate_min_norm(typename Scalar::Scalar& min_norm, math::Vector3i& coordinates,
		const Eigen::Tensor<Scalar, 3, Eigen::ColMajor>& vector_field);
/**
 * Locates the maximum in the given field.
 * @param[out] maximum the maximum coefficient
 * @param[out] coordinates coordinate of the maximum coefficient
 * @param[in] scalar_field the scalar field to look at
 */
template<typename Scalar>
void locate_maximum(Scalar& maximum, math::Vector2i& coordinates,
		const Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>& scalar_field);
/**
 * Locates the maximum in the given field.
 * @overload
 */
template<typename Scalar>
void locate_maximum(Scalar& maximum, math::Vector3i& coordinates,
		const Eigen::Tensor<Scalar, 3, Eigen::ColMajor>& scalar_field);

template<typename Scalar>
inline
Scalar maximum(const Eigen::Tensor<Scalar, 3, Eigen::ColMajor>& scalar_field){
	return static_cast<Eigen::Tensor<Scalar,0,Eigen::ColMajor> >(scalar_field.maximum())(0);
}

template<typename Scalar>
inline
Scalar maximum(const Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>& scalar_field){
	return scalar_field.maxCoeff();
}

template<typename Scalar>
inline
Scalar minimum(const Eigen::Tensor<Scalar, 3, Eigen::ColMajor>& scalar_field){
	return static_cast<Eigen::Tensor<Scalar,0,Eigen::ColMajor> >(scalar_field.minimum())(0);
}

template<typename Scalar>
inline
Scalar minimum(const Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>& scalar_field){
	return scalar_field.minCoeff();
}

//uses traversal function/functor combo, otherwise the same as locate_max_norm
void locate_max_norm2(float& max_norm, math::Vector2i& coordinates, const math::MatrixXv2f& vector_field);

/**
 * Locate the maximum L2 norm (length) of the vector in the given field.
 * @param[out] min_norm length of the shortest vector
 * @param[out] coordinate the location of the shortest vector
 * @param[in] vector_field the field to look at
 */
template<typename Scalar>
typename Scalar::Scalar max_norm(const Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>& vector_field);
/**
 * Locate the maximum L2 norm (length) of the vector in the given field.
 * @override
 */
template<typename Scalar>
typename Scalar::Scalar max_norm(const Eigen::Tensor<Scalar, 3, Eigen::ColMajor>& vector_field);
/**
 * Locate the minimum L2 norm (length) of the vector in the given field.
 * @param[out] min_norm length of the shortest vector
 * @param[out] coordinate the location of the shortest vector
 * @param[in] vector_field the field to look at
 */
template<typename Scalar>
typename Scalar::Scalar min_norm(const Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>& vector_field);
/**
 * Locate the minimum L2 norm (length) of the vector in the given field.
 * @override
 */
template<typename Scalar>
typename Scalar::Scalar min_norm(const Eigen::Tensor<Scalar, 3, Eigen::ColMajor>& vector_field);

template<typename Scalar>
Scalar mean(const Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>& field);

template<typename Scalar>
Scalar mean(const Eigen::Tensor<Scalar, 3, Eigen::ColMajor>& field);

template<typename Scalar>
Scalar std(const Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>& field);

template<typename Scalar>
Scalar std(const Eigen::Tensor<Scalar, 3, Eigen::ColMajor>& field);

float ratio_of_vector_lengths_above_threshold(const math::MatrixXv2f& vector_field, float threshold);
float mean_vector_length(const math::MatrixXv2f& vector_field);
void mean_and_std_vector_length(float& mean, float& standard_deviation, const math::MatrixXv2f& vector_field);

}


//
// Created by Hamza El-Kebir on 4/18/21.
//

#ifndef LODESTAR_TRANSFERFUNCTION_HPP
#define LODESTAR_TRANSFERFUNCTION_HPP

#include "SystemStateless.hpp"
#include "StateSpace.hpp"
#include "DiscreteStateSpace.hpp"
#include "Lodestar/analysis/BilinearTransformation.hpp"
#include "Lodestar/LodestarErrors.hpp"

#include <Eigen/Dense>

#ifdef LS_USE_GINAC

#include <ginac/ginac.h>

#undef Mutable

#endif

namespace ls {
    namespace systems {
        class TransferFunction : SystemStateless {
        public:
            /**
             * @brief Default constructor.
             */
            TransferFunction() : num_(Eigen::MatrixXd::Ones(1, 1)),
                                 den_(Eigen::MatrixXd::Ones(1, 1))
            {}

            /**
             * @brief Copy constructor.
             *
             * @param other Transfer function to copy.
             */
            TransferFunction(const TransferFunction &other);

            /**
             * @brief Constructs a transfer function from a vector of numerator
             * and denominator coefficients.
             *
             * @note Coefficients are in descending order (starting with the
             * monomial with highest power).
             *
             * @param num Numerator coefficients.
             * @param den Denominator coefficients.
             */
            TransferFunction(const Eigen::MatrixXd &num,
                             const Eigen::MatrixXd &den) : num_(num), den_(den)
            {}

            /**
             * @brief Gets numerator coefficients.
             *
             * @return Numerator coefficients.
             */
            Eigen::MatrixXd getNum() const;

            /**
             * @brief Sets numerator coefficients.
             *
             * @param num Numerator coefficients.
             */
            void setNum(const Eigen::MatrixXd &num);

            /**
             * Returns polynomial degree of the numerator.
             *
             * @return Numerator degree.
             */
            long getNumDegree() const;

            /**
             * @brief Gets denominator coefficients.
             *
             * @return Denominator coefficients.
             */
            Eigen::MatrixXd getDen() const;

            /**
             * @brief Set denominator coefficients.
             *
             * @param den Denominator coefficients.
             */
            void setDen(const Eigen::MatrixXd &den);

            /**
             * Returns polynomial degree of the denominator.
             *
             * @return denominator degree.
             */
            long getDenDegree() const;

            /**
             * @brief Normalizes transfer function in place.
             */
            void normalizeInPlace();

            /**
             * @brief Creates normalized transfer function.
             *
             * @return Normalized transfer function.
             */
            TransferFunction normalized() const;

            /**
             * @brief Converts transfer function to state space object.
             *
             * @return TState space object.
             */
            StateSpace<> toStateSpace() const;

            /**
             * @brief Converts transfer function to discrete-time state space
             * object.
             *
             * @note This method uses the backward difference method by default.
             *
             * @see ls::analysis::BilinearTransformation::c2dBwdDiff
             *
             * @param dt Sampling period.
             * @return Discrete-time state space system.
             */
            StateSpace<> toDiscreteStateSpace(double dt) const;

            /**
             * @brief Convert transfer function to discrete-time state space
             * object.
             *
             * This method adds the option to set \p alpha in the generalized
             * bilinear transform.
             *
             * @param dt Sampling period.
             * @param alpha Generalized bilinear transform parameter.
             * @return Discrete-time state space system.
             */
            StateSpace<> toDiscreteStateSpace(double dt, double alpha) const;

#ifdef LS_USE_GINAC

            /**
             * @brief Constructs a transfer function from the given symbolic
             * expression.
             *
             * @param tf Transfer function expression.
             * @param symbol Symbol used for the independent variable.
             */
            TransferFunction(const GiNaC::ex &tf, const GiNaC::ex &symbol);

            /**
             * @brief Generates a symbolic expression of the numerator
             * polynomial.
             *
             * @param symbol Symbol to use for the independent variable.
             *
             * @return GiNaC expression of the numerator polynomial.
             */
            GiNaC::ex getNumExpression(const GiNaC::ex &symbol) const;

            /**
             * @brief Generates a symbolic expression of the denominator
             * polynomial.
             *
             * @param symbol Symbol to use for the independent variable.
             *
             * @return GiNaC expression of the denominator polynomial.
             */
            GiNaC::ex getDenExpression(const GiNaC::ex &symbol) const;

            /**
             * @brief Generates a symbolic expression of the transfer function.
             *
             * @param symbol Symbol to use for the independent variable.
             * @return GiNaC expression of the transfer function.
             */
            GiNaC::ex getExpression(const GiNaC::ex &symbol) const;

            /**
             * @brief Copies transfer function coefficients from the given
             * symbolic expression.
             *
             * @param tf Transfer function expression.
             * @param symbol Symbol used for the independent variable.
             */
            void
            copyFromExpression(const GiNaC::ex &tf, const GiNaC::ex &symbol);

#endif

        private:
            Eigen::MatrixXd num_, /// Numerator coefficients.
            den_; /// Denominator coefficients.
        };
    }
}

#endif //LODESTAR_TRANSFERFUNCTION_HPP


//
// Multivariate Guassian Class.
// Original from Humphrey Hu
// (https://github.com/Humhu/argus_utils/blob/devel/include/argus_utils/random/MultivariateGaussian.hpp)
// Adapted by Arun Venkatraman
//

#pragma once

#include <Eigen/Dense>
#include <Eigen/Cholesky>

#include <cassert>
#include <random>

namespace math
{

class MultivariateGaussian
{
public:

	// Seeds the engine using a true random number. Sets mean_ to zero
	// and covariance to identity.
	MultivariateGaussian(const int dim);

	// Seeds the engine using the specified seed. Sets mean_ to zero
	// and _covariance to identity.
	MultivariateGaussian(const int dim, const int seed);

	// Sets the mean to mu and covariance to sigma.
	MultivariateGaussian(const Eigen::VectorXd& mu, const Eigen::MatrixXd& sigma);

	// Sets the mean to mu and covariance to sigma. Seeds the engine using a specified seed.
	MultivariateGaussian(const Eigen::VectorXd& mu, const Eigen::MatrixXd& sigma, const int seed);

	MultivariateGaussian(const MultivariateGaussian& other);
	MultivariateGaussian& operator=(const MultivariateGaussian& other);

    void set_mean(const Eigen::VectorXd& mu);
    void set_covariance(const Eigen::MatrixXd& sigma);
	void set_information(const Eigen::MatrixXd& inv_sigma);

	unsigned int get_dimension() const { return mean_.size(); }
	const Eigen::VectorXd& mean() const { return mean_; }
	const Eigen::MatrixXd covariance() const { return llt_.reconstructedMatrix(); }
	const Eigen::MatrixXd cholesky() const { return llt_.matrixL(); }

	// Generate a sample truncated at a specified number of standard deviations.
	Eigen::VectorXd sample(double max_var = 3.0);

	// Evaluate the multivariate normal PDF for the specified sample.
    double evaluate_probability(const Eigen::VectorXd& x) const;

protected:

    std::mt19937 gen_;
	std::normal_distribution<double> distribution_;

    // Mean of the distribution.
	Eigen::VectorXd mean_;
    // Store covariance as its cholesky decomposition.
	Eigen::LLT<Eigen::MatrixXd> llt_;

    // Normalization constant;
	double z_;

	void initialize_cov(const Eigen::MatrixXd& cov);
};

}


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

#define BOOST_TEST_MODULE polynomial_arithmetic_test

#include <vector>
#include <cstdint>

#include <boost/test/unit_test.hpp>
#include <boost/test/data/test_case.hpp>
#include <boost/test/data/monomorphic.hpp>

#include <nil/crypto3/algebra/fields/arithmetic_params/bls12.hpp>

#include <nil/crypto3/math/polynomial/basic_operations.hpp>
#include <nil/crypto3/math/polynomial/xgcd.hpp>

using namespace nil::crypto3::algebra;
using namespace nil::crypto3::math;

typedef fields::bls12<381> FieldType;

BOOST_AUTO_TEST_SUITE(polynomial_addition_test_suite)

BOOST_AUTO_TEST_CASE(polynomial_addition_equal) {
    std::vector<typename FieldType::value_type> a = {1, 3, 4, 25, 6, 7, 7, 2};
    std::vector<typename FieldType::value_type> b = {9, 3, 11, 14, 7, 1, 5, 8};
    std::vector<typename FieldType::value_type> c(1, FieldType::value_type::zero());

    _polynomial_addition(c, a, b);

    std::vector<typename FieldType::value_type> c_ans = {10, 6, 15, 39, 13, 8, 12, 10};

    for (std::size_t i = 0; i < c.size(); ++i) {
        BOOST_CHECK_EQUAL(c_ans[i].data, c[i].data);
    }
}

BOOST_AUTO_TEST_CASE(polynomial_addition_long_a) {

    std::vector<typename FieldType::value_type> a = {1, 3, 4, 25, 6, 7, 7, 2};
    std::vector<typename FieldType::value_type> b = {9, 3, 11, 14, 7};
    std::vector<typename FieldType::value_type> c(1, FieldType::value_type::zero());

    _polynomial_addition(c, a, b);

    std::vector<typename FieldType::value_type> c_ans = {10, 6, 15, 39, 13, 7, 7, 2};

    for (std::size_t i = 0; i < c.size(); i++) {
        BOOST_CHECK_EQUAL(c_ans[i].data, c[i].data);
    }
}

BOOST_AUTO_TEST_CASE(polynomial_addition_long_b) {

    std::vector<typename FieldType::value_type> a = {1, 3, 4, 25, 6};
    std::vector<typename FieldType::value_type> b = {9, 3, 11, 14, 7, 1, 5, 8};
    std::vector<typename FieldType::value_type> c(1, FieldType::value_type::zero());

    _polynomial_addition(c, a, b);

    std::vector<typename FieldType::value_type> c_ans = {10, 6, 15, 39, 13, 1, 5, 8};

    for (std::size_t i = 0; i < c.size(); i++) {
        BOOST_CHECK_EQUAL(c_ans[i].data, c[i].data);
    }
}

BOOST_AUTO_TEST_CASE(polynomial_addition_zero_a) {

    std::vector<typename FieldType::value_type> a = {0, 0, 0};
    std::vector<typename FieldType::value_type> b = {1, 3, 4, 25, 6, 7, 7, 2};
    std::vector<typename FieldType::value_type> c(1, FieldType::value_type::zero());

    _polynomial_addition(c, a, b);

    std::vector<typename FieldType::value_type> c_ans = {1, 3, 4, 25, 6, 7, 7, 2};

    for (std::size_t i = 0; i < c.size(); i++) {
        BOOST_CHECK_EQUAL(c_ans[i].data, c[i].data);
    }
}

BOOST_AUTO_TEST_CASE(polynomial_addition_zero_b) {

    std::vector<typename FieldType::value_type> a = {1, 3, 4, 25, 6, 7, 7, 2};
    std::vector<typename FieldType::value_type> b = {0, 0, 0};
    std::vector<typename FieldType::value_type> c(1, FieldType::value_type::zero());

    _polynomial_addition(c, a, b);

    std::vector<typename FieldType::value_type> c_ans = {1, 3, 4, 25, 6, 7, 7, 2};

    for (std::size_t i = 0; i < c.size(); i++) {
        BOOST_CHECK_EQUAL(c_ans[i].data, c[i].data);
    }
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(polynomial_subtraction_test_suite)

BOOST_AUTO_TEST_CASE(polynomial_subtraction_equal) {

    std::vector<typename FieldType::value_type> a = {1, 3, 4, 25, 6, 7, 7, 2};
    std::vector<typename FieldType::value_type> b = {9, 3, 11, 14, 7, 1, 5, 8};
    std::vector<typename FieldType::value_type> c(1, FieldType::value_type::zero());

    _polynomial_subtraction(c, a, b);

    std::vector<typename FieldType::value_type> c_ans = {-8, 0, -7, 11, -1, 6, 2, -6};

    for (std::size_t i = 0; i < c.size(); i++) {
        BOOST_CHECK_EQUAL(c_ans[i].data, c[i].data);
    }
}

BOOST_AUTO_TEST_CASE(polynomial_subtraction_long_a) {

    std::vector<typename FieldType::value_type> a = {1, 3, 4, 25, 6, 7, 7, 2};
    std::vector<typename FieldType::value_type> b = {9, 3, 11, 14, 7};
    std::vector<typename FieldType::value_type> c(1, FieldType::value_type::zero());

    _polynomial_subtraction(c, a, b);

    std::vector<typename FieldType::value_type> c_ans = {-8, 0, -7, 11, -1, 7, 7, 2};

    for (std::size_t i = 0; i < c.size(); i++) {
        BOOST_CHECK_EQUAL(c_ans[i].data, c[i].data);
    }
}

BOOST_AUTO_TEST_CASE(polynomial_subtraction_long_b) {

    std::vector<typename FieldType::value_type> a = {1, 3, 4, 25, 6};
    std::vector<typename FieldType::value_type> b = {9, 3, 11, 14, 7, 1, 5, 8};
    std::vector<typename FieldType::value_type> c(1, FieldType::value_type::zero());

    _polynomial_subtraction(c, a, b);

    std::vector<typename FieldType::value_type> c_ans = {-8, 0, -7, 11, -1, -1, -5, -8};

    for (std::size_t i = 0; i < c.size(); i++) {
        BOOST_CHECK_EQUAL(c_ans[i].data, c[i].data);
    }
}

BOOST_AUTO_TEST_CASE(polynomial_subtraction_zero_a) {

    std::vector<typename FieldType::value_type> a = {0, 0, 0};
    std::vector<typename FieldType::value_type> b = {1, 3, 4, 25, 6, 7, 7, 2};
    std::vector<typename FieldType::value_type> c(1, FieldType::value_type::zero());

    _polynomial_subtraction(c, a, b);

    std::vector<typename FieldType::value_type> c_ans = {-1, -3, -4, -25, -6, -7, -7, -2};

    for (std::size_t i = 0; i < c.size(); i++) {
        BOOST_CHECK_EQUAL(c_ans[i].data, c[i].data);
    }
}

BOOST_AUTO_TEST_CASE(polynomial_subtraction_zero_b) {

    std::vector<typename FieldType::value_type> a = {1, 3, 4, 25, 6, 7, 7, 2};
    std::vector<typename FieldType::value_type> b = {0, 0, 0};
    std::vector<typename FieldType::value_type> c(1, FieldType::value_type::zero());

    _polynomial_subtraction(c, a, b);

    std::vector<typename FieldType::value_type> c_ans = {1, 3, 4, 25, 6, 7, 7, 2};

    for (std::size_t i = 0; i < c.size(); i++) {
        BOOST_CHECK_EQUAL(c_ans[i].data, c[i].data);
    }
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(polynomial_multiplication_test_suite)

BOOST_AUTO_TEST_CASE(polynomial_multiplication_long_a) {

    std::vector<typename FieldType::value_type> a = {5, 0, 0, 13, 0, 1};
    std::vector<typename FieldType::value_type> b = {13, 0, 1};
    std::vector<typename FieldType::value_type> c(1, FieldType::value_type::zero());

    _polynomial_multiplication<FieldType>(c, a, b);

    std::vector<typename FieldType::value_type> c_ans = {65, 0, 5, 169, 0, 26, 0, 1};

    for (std::size_t i = 0; i < c.size(); i++) {
        BOOST_CHECK_EQUAL(c_ans[i].data, c[i].data);
    }
}

BOOST_AUTO_TEST_CASE(polynomial_multiplication_long_b) {

    std::vector<typename FieldType::value_type> a = {13, 0, 1};
    std::vector<typename FieldType::value_type> b = {5, 0, 0, 13, 0, 1};
    std::vector<typename FieldType::value_type> c(1, FieldType::value_type::zero());

    _polynomial_multiplication<FieldType>(c, a, b);

    std::vector<typename FieldType::value_type> c_ans = {65, 0, 5, 169, 0, 26, 0, 1};

    for (std::size_t i = 0; i < c.size(); i++) {
        BOOST_CHECK_EQUAL(c_ans[i].data, c[i].data);
    }
}

BOOST_AUTO_TEST_CASE(polynomial_multiplication_zero_a) {

    std::vector<typename FieldType::value_type> a = {0};
    std::vector<typename FieldType::value_type> b = {5, 0, 0, 13, 0, 1};
    std::vector<typename FieldType::value_type> c(1, FieldType::value_type::zero());

    _polynomial_multiplication<FieldType>(c, a, b);

    std::vector<typename FieldType::value_type> c_ans = {0};

    for (std::size_t i = 0; i < c.size(); i++) {
        BOOST_CHECK_EQUAL(c_ans[i].data, c[i].data);
    }
}

BOOST_AUTO_TEST_CASE(polynomial_multiplication_zero_b) {

    std::vector<typename FieldType::value_type> a = {5, 0, 0, 13, 0, 1};
    std::vector<typename FieldType::value_type> b = {0};
    std::vector<typename FieldType::value_type> c(1, FieldType::value_type::zero());

    _polynomial_multiplication<FieldType>(c, a, b);

    std::vector<typename FieldType::value_type> c_ans = {0};

    for (std::size_t i = 0; i < c.size(); i++) {
        BOOST_CHECK_EQUAL(c_ans[i].data, c[i].data);
    }
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_CASE(polynomial_division) {

    std::vector<typename FieldType::value_type> a = {5, 0, 0, 13, 0, 1};
    std::vector<typename FieldType::value_type> b = {13, 0, 1};

    std::vector<typename FieldType::value_type> Q(1, FieldType::value_type::zero());
    std::vector<typename FieldType::value_type> R(1, FieldType::value_type::zero());

    _polynomial_division<FieldType>(Q, R, a, b);

    std::vector<typename FieldType::value_type> Q_ans = {0, 0, 0, 1};
    std::vector<typename FieldType::value_type> R_ans = {5};

    for (std::size_t i = 0; i < Q.size(); i++) {
        BOOST_CHECK_EQUAL(Q_ans[i].data, Q[i].data);
    }
    for (std::size_t i = 0; i < R.size(); i++) {
        BOOST_CHECK_EQUAL(R_ans[i].data, R[i].data);
    }
}

BOOST_AUTO_TEST_CASE(extended_gcd) {

    std::vector<typename FieldType::value_type> a = {0, 0, 0, 0, 1};
    std::vector<typename FieldType::value_type> b = {1, -6, 11, -6};

    std::vector<typename FieldType::value_type> pg(1, FieldType::value_type::zero());
    std::vector<typename FieldType::value_type> pu(1, FieldType::value_type::zero());
    std::vector<typename FieldType::value_type> pv(1, FieldType::value_type::zero());

    extended_euclidean<FieldType>(a, b, pg, pu, pv);

    std::vector<typename FieldType::value_type> pv_ans = {1, 6, 25, 90};

    for (std::size_t i = 0; i < pv.size(); i++) {
        BOOST_CHECK_EQUAL(pv_ans[i].data, pv[i].data);
    }
}


/*
Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/)
Copyright 2017-2019 University of Huddersfield.
Licensed under the BSD-3 License.
See license.md file in the project root for full license information.
This project has received funding from the European Research Council (ERC)
under the European Union’s Horizon 2020 research and innovation programme
(grant agreement No 725899).
*/

// modified version of Normalization.hpp code
#pragma once

#include "../util/FluidEigenMappings.hpp"
#include "../../data/TensorTypes.hpp"
#include <Eigen/Core>
#include <cassert>
#include <cmath>

namespace fluid {
namespace algorithm {

class RobustScaling
{
public:
  using ArrayXd = Eigen::ArrayXd;
  using ArrayXXd = Eigen::ArrayXXd;

  void init(double low, double high, RealMatrixView in)
  {
    using namespace Eigen;
    using namespace _impl;
    const double epsilon = std::numeric_limits<double>::epsilon();
    mLow = low;
    mHigh = high;
    ArrayXXd input = asEigen<Array>(in);
    mDataLow.resize(input.cols());
    mDataHigh.resize(input.cols());
    mMedian.resize(input.cols());
    mRange.resize(input.cols());
    index length = input.rows();
    for (index i = 0; i < input.cols(); i++)
    {
      ArrayXd sorted = input.col(i);
      std::sort(sorted.data(), sorted.data() + length);
      mMedian(i) = sorted(lrint(0.5 * (length - 1)));
      mDataLow(i) = sorted(lrint((mLow / 100.0) * (length - 1)));
      mDataHigh(i) = sorted(lrint((mHigh / 100.0) * (length - 1)));
    }
    mRange = mDataHigh - mDataLow;
    mRange = mRange.max(epsilon);
    mInitialized = true;
  }

  void init(double low, double high, RealVectorView dataLow,
            RealVectorView dataHigh, RealVectorView median,
            RealVectorView range)
  {
    using namespace Eigen;
    using namespace _impl;
    const double epsilon = std::numeric_limits<double>::epsilon();
    mLow = low;
    mHigh = high;
    mDataLow = asEigen<Array>(dataLow);
    mDataHigh = asEigen<Array>(dataHigh);
    mMedian = asEigen<Array>(median);
    mRange = asEigen<Array>(range);
    mRange =
        mRange.max(epsilon); // in case it is imported from the outside world
    mInitialized = true;
  }

  void processFrame(const RealVectorView in, RealVectorView out,
                    bool inverse = false) const
  {
    using namespace Eigen;
    using namespace _impl;
    ArrayXd input = asEigen<Array>(in);
    ArrayXd result;
    if (!inverse) { result = (input - mMedian) / mRange; }
    else
    {
      result = (input * mRange) + mMedian;
    }
    out = asFluid(result);
  }

  void process(const RealMatrixView in, RealMatrixView out,
               bool inverse = false) const
  {
    using namespace Eigen;
    using namespace _impl;
    ArrayXXd input = asEigen<Array>(in);
    ArrayXXd result;
    if (!inverse)
    {
      result = (input.rowwise() - mMedian.transpose());
      result = result.rowwise() / mRange.transpose();
    }
    else
    {
      result = (input.rowwise() * mRange.transpose());
      result = (result.rowwise() + mMedian.transpose());
    }
    out = asFluid(result);
  }

  void setLow(double low) { mLow = low; }
  void setHigh(double high) { mHigh = high; }
  bool initialized() const { return mInitialized; }

  double getLow() const { return mLow; }
  double getHigh() const { return mHigh; }

  void getDataLow(RealVectorView out) const
  {
    using namespace _impl;
    out = asFluid(mDataLow);
  }

  void getDataHigh(RealVectorView out) const
  {
    using namespace _impl;
    out = asFluid(mDataHigh);
  }

  void getMedian(RealVectorView out) const
  {
    using namespace _impl;
    out = asFluid(mMedian);
  }

  void getRange(RealVectorView out) const
  {
    using namespace _impl;
    out = asFluid(mRange);
  }

  index dims() const { return mMedian.size(); }
  index size() const { return 1; }

  void clear()
  {
    mLow = 0;
    mHigh = 1.0;
    mMedian.setZero();
    mRange.setZero();
    mRange.setZero();
    mInitialized = false;
  }

  double  mLow{0.0};
  double  mHigh{1.0};
  ArrayXd mDataHigh;
  ArrayXd mDataLow;
  ArrayXd mMedian;
  ArrayXd mRange;
  bool    mInitialized{false};
};
}; // namespace algorithm
}; // namespace fluid


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

#include "homography/compute_homography_model.hpp"
#include "homography/compute_homography_error.hpp"
#include "ransac.hpp"

using namespace std;
using namespace Eigen;

void random_measurements(Matrix3d& H, vector<Point2f>& src, vector<Point2f>& dst, int n) {
  src.clear();
  dst.clear();
  H = Matrix3d::Random();
  H /= H(2,2);
  VectorXd x = 1000*VectorXd::Random(n);
  VectorXd y = 1000*VectorXd::Random(n);
  for(int i = 0; i < n; i++) {
    Vector3d p(x(i), y(i), 1);
    Vector3d pp = H*p+Vector3d(1.*rand()/RAND_MAX, 1.*rand()/RAND_MAX, 0);
    src.emplace_back(p.x()/p.z(), p.y()/p.z());
    dst.emplace_back(pp.x()/pp.z(), pp.y()/pp.z());
  }
}

int main() {
  ComputeHomographyModel chm;
  ComputeHomographyError che;
  vector<HomographyMeasurement> measurements;

  measurements.emplace_back(Point2f(0,0), Point2f(1,1));
  measurements.emplace_back(Point2f(1,1), Point2f(2,2));
  measurements.emplace_back(Point2f(2,1), Point2f(3,4));
  measurements.emplace_back(Point2f(4,3), Point2f(1,5));

  auto start = chrono::high_resolution_clock::now();
  HomographyModel hm = chm(measurements);
  auto finish = chrono::high_resolution_clock::now();

  // cout << che(hm, measurements[0]) << endl;

  cout << "Simple homography calculation\n";
  cout << "Both matrices should be equal\n";
  cout << "Ours: " << chrono::duration<double>(finish-start).count() << " s" << endl;
  hm.print();

  vector<Point2f> src, dst;
  src.emplace_back(0,0);
  src.emplace_back(1,1);
  src.emplace_back(2,1);
  src.emplace_back(4,3);
  dst.emplace_back(1,1);
  dst.emplace_back(2,2);
  dst.emplace_back(3,4);
  dst.emplace_back(1,5);

  start = chrono::high_resolution_clock::now();
  Mat H = findHomography(src, dst);
  finish = chrono::high_resolution_clock::now();
  cout << "OpenCV's: " << chrono::duration<double>(finish-start).count() << " s" << endl;
  cout << H << endl;

  Matrix3d real_H;
  double opencv = 0, our = 0;
  for(int i = 0; i < 1000; i++) {
    random_measurements(real_H, src, dst, 10000);
    start = chrono::high_resolution_clock::now();
    H = findHomography(src, dst, CV_RANSAC);
    finish = chrono::high_resolution_clock::now();
    opencv += chrono::duration<double>(finish-start).count();
    // cout << "Real H:\n" << real_H << endl;
    // cout << "OpenCV's H: " << chrono::duration<double>(finish - start).count()
        //  << " s" << endl;
    // cout << H << endl;

    measurements.clear();
    for (size_t i = 0; i < src.size(); i++) {
      measurements.emplace_back(src[i], dst[i]);
    }

    start = chrono::high_resolution_clock::now();
    auto my_H =
        ransac<HomographyModel, HomographyMeasurement, ComputeHomographyModel,
               ComputeHomographyError, 4>(measurements);
    finish = chrono::high_resolution_clock::now();
    our += chrono::duration<double>(finish-start).count();

    // cout << "Our H: " << chrono::duration<double>(finish - start).count()
        //  << " s" << endl;
    // my_H.print();
  }
  cout << "OpenCV's: " << opencv << " s" << endl;
  cout << "Ours: " << our << " s" << endl;

  return EXIT_SUCCESS;
}


// Copyright Paul A. Bristow 2016
// Copyright John Z. Maddock 2016

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

/*! brief Example of using Lambert W function to compute current through a diode connected transistor with preset series resistance.
  \details T. C. Banwell and A. Jayakumar,
  Exact analytical solution of current flow through diode with series resistance,
  Electron Letters, 36(4):291-2 (2000)
  DOI:  doi.org/10.1049/el:20000301

  The current through a diode connected NPN bipolar junction transistor (BJT)
  type 2N2222 (See https://en.wikipedia.org/wiki/2N2222 and
  https://www.fairchildsemi.com/datasheets/PN/PN2222.pdf Datasheet)
  was measured, for a voltage between 0.3 to 1 volt, see Fig 2 for a log plot,
  showing a knee visible at about 0.6 V.

  The transistor parameter isat was estimated to be 25 fA and the ideality factor = 1.0.
  The intrinsic emitter resistance re was estimated from the rsat = 0 data to be 0.3 ohm.

  The solid curves in Figure 2 are calculated using equation 5 with rsat included with re.

  http://www3.imperial.ac.uk/pls/portallive/docs/1/7292572.PDF
*/

#include <boost/math/special_functions/lambert_w.hpp>
using boost::math::lambert_w0;

#include <iostream>
// using std::cout;
// using std::endl;
#include <exception>
#include <stdexcept>
#include <string>
#include <array>
#include <vector>

/*!
Compute thermal voltage as a function of temperature,
about 25 mV at room temperature.
https://en.wikipedia.org/wiki/Boltzmann_constant#Role_in_semiconductor_physics:_the_thermal_voltage

\param temperature Temperature (degrees centigrade).
*/
const double v_thermal(double temperature)
{
  constexpr const double boltzmann_k = 1.38e-23; // joules/kelvin.
  const double charge_q = 1.6021766208e-19; // Charge of an electron (columb).
  double temp =+ 273; // Degrees C to K.
  return boltzmann_k * temp / charge_q;
} // v_thermal

/*!
Banwell & Jayakumar, equation 2
*/
double i(double isat, double vd, double vt, double nu)
{
  double i = isat * (exp(vd / (nu * vt)) - 1);
  return i;
} //

/*!

Banwell & Jayakumar, Equation 4.
i current flow = isat
v voltage source.
isat reverse saturation current in equation 4.
(might implement equation 4 instead of simpler equation 5?).
vd voltage drop = v - i* rs  (equation 1).
vt  thermal voltage, 0.0257025 = 25 mV.
nu junction ideality factor (default = unity), also known as the emission coefficient.
re intrinsic emitter resistance, estimated to be 0.3 ohm from low current.
rsat reverse saturation current

\param v Voltage V to compute current I(V).
\param vt Thermal voltage, for example 0.0257025 = 25 mV, computed from boltzmann_k * temp / charge_q;
\param rsat Resistance in series with the diode.
\param re Instrinsic emitter resistance (estimated to be 0.3 ohm from the Rs = 0 data)
\param isat Reverse saturation current (See equation 2).
\param nu Ideality factor (default = unity).

\returns I amp as function of V volt.

*/
double iv(double v, double vt, double rsat, double re, double isat, double nu = 1.)
{
  // V thermal 0.0257025 = 25 mV
  // was double i = (nu * vt/r) * lambert_w((i0 * r) / (nu * vt)); equ 5.

  rsat = rsat + re;
  double i = nu * vt / rsat;
  std::cout << "nu * vt / rsat = " << i << std::endl; // 0.000103223

  double x = isat * rsat / (nu * vt);
  std::cout << "isat * rsat / (nu * vt) = " << x << std::endl;

  double eterm = (v + isat * rsat) / (nu * vt);
  std::cout << "(v + isat * rsat) / (nu * vt) = " << eterm << std::endl;

  double e = exp(eterm);
  std::cout << "exp(eterm) = " << e << std::endl;

  double w0 = lambert_w0(x * e);
  std::cout << "w0 = " << w0 << std::endl;
  return i * w0 - isat;

} // double iv

std::array<double, 5> rss = {0., 2.18, 10., 51., 249};  // series resistance (ohm).
std::array<double, 8> vds = { 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9 };  // Diode voltage.

int main()
{
  try
  {
    std::cout << "Lambert W diode current example." << std::endl;

    //[lambert_w_diode_example_1
    double x = 0.01;
    //std::cout << "Lambert W (" << x << ") = " << lambert_w(x) << std::endl; // 0.00990147

    double nu = 1.0;                  // Assumed ideal.
    double vt = v_thermal(25);        // v thermal, Shockley equation, expect about 25 mV at room temperature.
    double boltzmann_k = 1.38e-23;    // joules/kelvin
    double temp = 273 + 25;
    double charge_q = 1.6e-19;        // column
    vt = boltzmann_k * temp / charge_q;
    std::cout << "V thermal "
       << vt << std::endl;            // V thermal 0.0257025 = 25 mV
    double rsat = 0.;
    double isat = 25.e-15;            //  25 fA;
    std::cout << "Isat = " << isat << std::endl;

    double re = 0.3;  // Estimated from slope of straight section of graph (equation 6).

    double v = 0.9;
    double icalc = iv(v, vt, 249., re, isat);

    std::cout << "voltage = " << v << ", current = " << icalc << ", " << log(icalc) << std::endl; // voltage = 0.9, current = 0.00108485, -6.82631
 //] [/lambert_w_diode_example_1]
  }
  catch (std::exception& ex)
  {
    std::cout << ex.what() << std::endl;
  }
}  // int main()

/*
   Output:
//[lambert_w_output_1
   Lambert W diode current example.
   V thermal 0.0257025
   Isat = 2.5e-14
   nu * vt / rsat = 0.000103099
   isat * rsat / (nu * vt) = 2.42486e-10
   (v + isat * rsat) / (nu * vt) = 35.016
   exp(eterm) = 1.61167e+15
   w0 = 10.5225
   voltage = 0.9, current = 0.00108485, -6.82631

//] [/lambert_w_output_1]
*/


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

#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795
#endif

using namespace Eigen;
using namespace std;

int main(int, char**)
{
  cout.precision(3);
  Matrix2f m = Matrix2f::Random();
m = (m + m.adjoint()).eval();
JacobiRotation<float> J;
J.makeJacobi(m, 0, 1);
cout << "Here is the matrix m:" << endl << m << endl;
m.applyOnTheLeft(0, 1, J.adjoint());
m.applyOnTheRight(0, 1, J);
cout << "Here is the matrix J' * m * J:" << endl << m << endl;
  return 0;
}


///////////////////////////////////////////////////////////////////////////////
//  Copyright Paul Bristow 2015.
//  Copyright Christopher Kormanyos 2015.
//  Copyright Nikhar Agrawal 2015.
//  Distributed under the Boost Software License,
//  Version 1.0. (See accompanying file LICENSE_1_0.txt
//  or copy at http://www.boost.org/LICENSE_1_0.txt)

//! \file
//!\brief Tests round-trip for fixed_point negatable with 5.2 binary digits.

#define BOOST_TEST_MODULE test_negatable_round_trip_digits02_005m2
#define BOOST_LIB_DIAGNOSTIC

#include <algorithm>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>

#include <boost/fixed_point/fixed_point.hpp>
#include <boost/test/included/unit_test.hpp>

namespace local
{
  // Define a binary fixed-point type with resolution 5 range -2
  typedef boost::fixed_point::negatable<5, -2> fixed_point_type;

  bool round_trip(const fixed_point_type& x);

  // Here we need to invert Kahan's formula to ensure that all of
  // the binary fractional digits can be printed in decimal form.
  // In this case, we need to print two decimal digits10,
  // even tough we are only have two binary fractional digits2.

  // Begin with
  //   digits2 = 8 (i.e. what is required for two decimal digits10)
  // This gives
  //  digits10 = [(8 - 1) * 301] / 1000
  //  digits10 = 2107 / 1000 = 2

  // In this case, we simply hard-code the value of 2
  // for the number of decimal digits in the fixed-point field.

  // For an additional example using the fixed-point negatable class
  // with 2 decimal digits of precision, see also the test file
  // named "test_negatable_round_trip_digits10_002.cpp".

  BOOST_CONSTEXPR_OR_CONST int digits10 = int((long(8L - 1L) * 301L) / 1000L);

  volatile fixed_point_type object_to_be_instantiated_at_least_once;
}

bool local::round_trip(const local::fixed_point_type& x)
{
  using local::fixed_point_type;

  std::stringstream ss1;

  ss1.precision(local::digits10);

  ss1 << std::fixed
      << x;

  std::stringstream ss2(ss1.str());

  fixed_point_type y;
  ss2 >> y;

  const bool b(x == y);

  return b;
}

BOOST_AUTO_TEST_CASE(test_negatable_round_trip_digits02_005m2)
{
  using local::fixed_point_type;

  typedef fixed_point_type::float_type floating_point_type;

  boost::uint_fast16_t count;

  BOOST_CONSTEXPR_OR_CONST boost::uint_fast16_t number_of_test_cases = UINT16_C(255);

  bool b = true;

  // Test sequential values with 5.2 binary digits of precision
  // ranging from 0.25, 0.50, 0.75, ... 63.75.
  for(count = UINT16_C(1); ((count < number_of_test_cases) && b); ++count)
  {
    std::stringstream ss1;

    ss1 << std::fixed
        << std::setprecision(local::digits10)
        << (float(count) / 4.0F);

    std::string str(ss1.str());

    const fixed_point_type x(boost::lexical_cast<floating_point_type>(str));

    const bool next_test_result = local::round_trip(x);

    b = (b && next_test_result);
  }

  BOOST_CHECK_EQUAL(count, number_of_test_cases);

  BOOST_CHECK_EQUAL(b, true);
}


/*
 * Copyright (C) 2014-2015 DubinsPathPlanner.
 * Created by David Goodman <dagoodma@gmail.com>
 * Redistribution and use of this file is allowed according to the terms of the MIT license.
 * For details see the LICENSE file distributed with DubinsPathPlanner.
*/
#include <cmath>
#include <iostream>
#include <vector>
#include <ctime>
#include <cstdlib>

#include <gtest/gtest.h>
#include <Eigen/Dense>

#include <ogdf/basic/List.h>

#include <dpp/basic/Util.h>

using Eigen::Vector3d;
using Eigen::Vector2d;

using ogdf::List;
using ogdf::ListIterator;
using ogdf::ListConstIterator;
using ogdf::NodeArray;
using ogdf::DPoint;
using ogdf::DLine;
using ogdf::DSegment;

using std::vector;

// TODO parameterize data-driven tests in this file

#define DELTA           1E-2 // input perturbation constant
#define EPSILON_ERROR   1E-5  // output error threshold

// --------------------- myMod() -------------------
TEST(MyModTest, FmodEquivalenceComparison) {
    EXPECT_DOUBLE_EQ(fmod(0.0,2), dpp::myMod(0.0,2));
    EXPECT_DOUBLE_EQ(fmod(1.0,2), dpp::myMod(1.0,2));
    EXPECT_DOUBLE_EQ(fmod(1.0,2), dpp::myMod(1.0,2));
    EXPECT_DOUBLE_EQ(fmod(2.0,2), dpp::myMod(2.0,2));
    EXPECT_DOUBLE_EQ(fmod(2.1,2), dpp::myMod(2.1,2));
    EXPECT_DOUBLE_EQ(fmod(5,2), dpp::myMod(5,2));
}

TEST(MyModTest, RemovesNegatives) {
    EXPECT_DOUBLE_EQ(1, dpp::myMod(-1.0,2));
    EXPECT_DOUBLE_EQ(0, dpp::myMod(-2.0,2));
    EXPECT_DOUBLE_EQ(1.9, dpp::myMod(-2.1,2));
}

// --------------------- headingToAngle() -------------------
TEST(HeadingToAngleTest, Quadrants) {
    EXPECT_DOUBLE_EQ(M_PI/2.0, dpp::headingToAngle(0.0));
    EXPECT_DOUBLE_EQ(0.0, dpp::headingToAngle(M_PI/2.0));
    EXPECT_DOUBLE_EQ(3.0*M_PI/2.0, dpp::headingToAngle(M_PI));
    EXPECT_DOUBLE_EQ(M_PI, dpp::headingToAngle(3.0*M_PI/2.0));
}

// --------------------- angleToHeading() -------------------
TEST(AngleToHeadingTest, Quadrants) {
    EXPECT_DOUBLE_EQ(M_PI/2.0, dpp::angleToHeading(0.0));
    EXPECT_DOUBLE_EQ(0.0, dpp::angleToHeading(M_PI/2.0));
    EXPECT_DOUBLE_EQ(3.0*M_PI/2.0, dpp::angleToHeading(M_PI));
    EXPECT_DOUBLE_EQ(M_PI, dpp::angleToHeading(3.0*M_PI/2.0));
}

// --------------------- wrapAngle() -------------------
TEST(WrapAngleTest, NoWrap) {
    EXPECT_DOUBLE_EQ(0.0, dpp::wrapAngle(0.0));
    EXPECT_DOUBLE_EQ(M_PI, dpp::wrapAngle(M_PI));
    EXPECT_DOUBLE_EQ(3.0*M_PI/2.0, dpp::wrapAngle(3.0*M_PI/2.0));
}
TEST(WrapAngleTest, Wrap) {
    EXPECT_DOUBLE_EQ(0.0, dpp::wrapAngle(2.0*M_PI));
    EXPECT_DOUBLE_EQ(M_PI/2.0, dpp::wrapAngle(5.0*M_PI/2.0));
    EXPECT_NEAR(DELTA, dpp::wrapAngle(2.0*M_PI + DELTA), EPSILON_ERROR);
}
TEST(WrapAngleTest, TwoPiWraps) {
    double input=2*M_PI;
    double expectedOutput=0.0;
    EXPECT_NEAR(expectedOutput, dpp::wrapAngle(input), EPSILON_ERROR);
}
TEST(WrapAngleTest, BoundaryWrap) {
    double input=6.283195307179586;
    double expectedOutput=0.0;
    EXPECT_NEAR(expectedOutput, dpp::wrapAngle(input), EPSILON_ERROR);
}

// --------------------- angleBetween() -------------------
const Vector2d uAngle(1.0, 0.0);

TEST(AngleBetweenTest, SameAngle) {
    Vector2d v(1.0, 0.0);
    double expectedOutput = 0.0;
    double actualOutput = dpp::angleBetween(uAngle,v);
    EXPECT_DOUBLE_EQ(expectedOutput, actualOutput);
}

TEST(AngleBetweenTest, AcuteAngle) {
    Vector2d v(0.707, 0.707);
    double expectedOutput = dpp::degToRad(45);
    double actualOutput = dpp::angleBetween(uAngle,v);
    EXPECT_DOUBLE_EQ(expectedOutput, actualOutput);
}

TEST(AngleBetweenTest, ObtuseAngle) {
    Vector2d v(0.0, -1.0);
    //double expectedOutput = dpp::degToRad(270); // not clockwise-only
    double expectedOutput = dpp::degToRad(90);
    double actualOutput = dpp::angleBetween(uAngle,v);
    EXPECT_DOUBLE_EQ(expectedOutput, actualOutput);
}

// --------------------- angleOfLine() -------------------
TEST(AngleOfLineTest, HorizontalLine) {
    DLine line(DPoint(-1,0), DPoint(5,0));
    double expectedOutput = 0.0;
    double actualOutput = dpp::angleOfLine(line);

    EXPECT_DOUBLE_EQ(expectedOutput, actualOutput);
}

TEST(AngleOfLineTest, VerticalLine) {
    DLine line(DPoint(0,-1), DPoint(0,10));
    double expectedOutput = dpp::degToRad(90);
    double actualOutput = dpp::angleOfLine(line);

    EXPECT_DOUBLE_EQ(expectedOutput, actualOutput);
}

TEST(AngleOfLineTest, AnotherLine) {
    DLine line(DPoint(-1,-1), DPoint(-3,-3));
    double expectedOutput = dpp::degToRad(225);
    double actualOutput = dpp::angleOfLine(line);

    EXPECT_DOUBLE_EQ(expectedOutput, actualOutput);
}

// --------------------- headingBetween() -------------------
// note: headings are between points (not vectors), and are taken in the
//      clockwise direction from north
const Vector2d uHeading(0.0,0.0); // point at the origin

TEST(HeadingBetweenTest, AtanAtZero) {
    double x1=0,y1=0,x2=1,y2=0;
    EXPECT_DOUBLE_EQ(0.0, atan((y1 - y2)/(x2 - x1)));
}

TEST(HeadingBetweenTest, SamePoint) {
    Vector2d v(uHeading);
    EXPECT_DOUBLE_EQ(0.0, dpp::headingBetween(uHeading, v));
    EXPECT_DOUBLE_EQ(0.0, dpp::headingBetween(v, uHeading));
}

TEST(HeadingBetweenTest, QuadrantI) {
    // Test Case for Quadrant 1 Boundaries
    Vector2d v(
        0.0,
        1.0);
    EXPECT_DOUBLE_EQ(0.0, dpp::headingBetween(uHeading, v));

    v[0] = 1.0;
    v[1] = 1.0;
    EXPECT_DOUBLE_EQ(dpp::degToRad(45.0), dpp::headingBetween(uHeading,v));
}

TEST(HeadingBetweenTest, QuadrantII) {
    // Test Case for Quadrant 2 Boundaries
    Vector2d v(
        1.0,
        0.0);
    EXPECT_DOUBLE_EQ(dpp::degToRad(90.0), dpp::headingBetween(uHeading,v));

    v[0] = 1.0;
    v[1] = -1.0;
    EXPECT_DOUBLE_EQ(dpp::degToRad(135.0), dpp::headingBetween(uHeading,v));
}
TEST(HeadingBetweenTest, QuadrantIII) {
    // Test Case for Quadrant 3 Boundaries
    Vector2d v(
        0.0,
        -1.0);
    EXPECT_DOUBLE_EQ(dpp::degToRad(180.0), dpp::headingBetween(uHeading,v));

    v[0] = -1.0;
    v[1] = -1.0;
    EXPECT_DOUBLE_EQ(dpp::degToRad(225.0), dpp::headingBetween(uHeading,v));
}

TEST(HeadingBetweenTest, QuadrantIV) {
    // Test Case for Quadrant 4 Boundaries
    Vector2d v(
        -1.0,
        0.0);
    EXPECT_DOUBLE_EQ(dpp::degToRad(270.0), dpp::headingBetween(uHeading,v));

    v[0] = -1.0;
    v[1] = 1.0;
    EXPECT_DOUBLE_EQ(dpp::degToRad(315.0), dpp::headingBetween(uHeading,v));
}

TEST(HeadingBetweenTest, Various) {
    // Some various test cases
    Vector2d u(
        1.0,
        1.0),
        v(
        1.0,
        -1.0);
    EXPECT_DOUBLE_EQ(dpp::degToRad(180), dpp::headingBetween(u,v));
    EXPECT_DOUBLE_EQ(dpp::degToRad(0), dpp::headingBetween(v,u));
}

// --------------------- polarToCartesian() -------------------
const double expectedC = 0.707106781186547;

TEST(PolarToCartesianTest, AtOrigin) {
    DPoint expectedP(0,0);
    {
        DPoint actualP = dpp::polarToCartesian(0,0);

        EXPECT_EQ(expectedP, actualP);
    }
    {
        DPoint actualP = dpp::polarToCartesian(270,0);

        EXPECT_EQ(expectedP, actualP);
    }
}

TEST(PolarToCartesianTest, QuadrantI) {
    {
        DPoint expectedP(1,0);
        DPoint actualP = dpp::polarToCartesian(0,1);

        EXPECT_EQ(expectedP, actualP);
    }
    {
        DPoint expectedP(expectedC,expectedC);
        DPoint actualP = dpp::polarToCartesian(dpp::degToRad(45),1);

        EXPECT_EQ(expectedP, actualP);
    }
    {
        DPoint expectedP(0,1);
        DPoint actualP = dpp::polarToCartesian(dpp::degToRad(90),1);

        EXPECT_EQ(expectedP, actualP);
    }
}

TEST(PolarToCartesianTest, QuadrantII) {
    {
        DPoint expectedP(-expectedC,expectedC);
        DPoint actualP = dpp::polarToCartesian(dpp::degToRad(135),1);

        EXPECT_EQ(expectedP, actualP);
    }
    {
        DPoint expectedP(-1,0);
        DPoint actualP = dpp::polarToCartesian(dpp::degToRad(180),1);

        EXPECT_EQ(expectedP, actualP);
    }
}

TEST(PolarToCartesianTest, QuadrantIII) {
    {
        DPoint expectedP(-expectedC,-expectedC);
        DPoint actualP = dpp::polarToCartesian(dpp::degToRad(225),1);

        EXPECT_EQ(expectedP, actualP);
    }
    {
        DPoint expectedP(0,-2);
        DPoint actualP = dpp::polarToCartesian(dpp::degToRad(270),2);

        EXPECT_EQ(expectedP, actualP);
    }
}

TEST(PolarToCartesianTest, QuadrantIV) {
    {
        DPoint expectedP(expectedC,-expectedC);
        DPoint actualP = dpp::polarToCartesian(dpp::degToRad(315),1);

        EXPECT_EQ(expectedP, actualP);
    }
}

// --------------------- clearEdges() -------------------
using ogdf::node;
using ogdf::edge;
using ogdf::Graph;
using ogdf::GraphAttributes;

using ::testing::Test;
using ::testing::Values;
using ::testing::ValuesIn;
class ClearEdgesTest : public Test {
public:
    ClearEdgesTest()
        : m_G(),
          m_GA (m_G,
            GraphAttributes::nodeId) {}

    virtual ~ClearEdgesTest() { }

    virtual void SetUp() {
        // Add nodes
        node u = m_G.newNode();
        node v = m_G.newNode();
        m_nodeList.push_back(u);
        m_nodeList.push_back(v);

    } // InitializeScenario()

    virtual void TearDown() {
    } // TearDown()

    int GetIndex(node &u) {
        return m_GA.idNode(u);
    }

    node GetNode(int i) {
        i -= 1;
        if (i < m_G.numberOfNodes()) {
            return m_nodeList.at(i);
        }
        return nullptr;
    }

    int GetSize() {
        return m_G.numberOfEdges();
    }

protected:
    Graph m_G;
    GraphAttributes m_GA;
    vector<node> m_nodeList;

}; // class ClearEdgesTest

TEST_F(ClearEdgesTest, NoEdges) {
    // Nothing is cleared
    EXPECT_EQ(0, GetSize());
    dpp::clearEdges(m_G);
    EXPECT_EQ(0, GetSize());
}

TEST_F(ClearEdgesTest, OneEdge) {
    EXPECT_EQ(0, GetSize());

    // Add an edge
    edge e = m_G.newEdge(GetNode(1), GetNode(2));
    EXPECT_EQ(1, GetSize());

    // Delete it
    dpp::clearEdges(m_G);
    EXPECT_EQ(0, GetSize());
}

TEST_F(ClearEdgesTest, TwoEdges) {
    EXPECT_EQ(0, GetSize());

    // Add two edges
    edge e1 = m_G.newEdge(GetNode(1), GetNode(2));
    edge e2 = m_G.newEdge(GetNode(2), GetNode(1));
    EXPECT_EQ(2, GetSize());

    // Delete them
    dpp::clearEdges(m_G);
    EXPECT_EQ(0, GetSize());
}

// --------------------- graphsAreEquivalent() -------------------

// These nodes are used in several tests
const List<DPoint> nodes {
    {0, 0},
    {5.1, 0.0},
    {5.1, -3.3}
};

class GraphsAreEquivalentTest : public Test {
public:
    GraphsAreEquivalentTest()
        : m_G(),
          m_GA (m_G, DPP_GRAPH_ATTRIBUTES ),
          m_Gcopy(),
          m_GAcopy(m_Gcopy, DPP_GRAPH_ATTRIBUTES)
    { }

    virtual ~GraphsAreEquivalentTest() { }

    virtual void SetUp() {
        // Add nodes to original
        int i = 1;
        ListConstIterator<DPoint> iter;
        for ( iter = nodes.begin(); iter != nodes.end(); iter++ ) {
            DPoint Pu = *iter;
            node u = m_G.newNode();
            m_GA.x(u) = Pu.m_x;
            m_GA.y(u) = Pu.m_y;
            m_GA.idNode(u) = i;
            i++;
        }

        // Add the same nodes to copy
        int icopy = 1;
        for ( iter = nodes.begin(); iter != nodes.end(); iter++ ) {
            DPoint Pu = *iter;
            node u = m_Gcopy.newNode();
            m_GAcopy.x(u) = Pu.m_x;
            m_GAcopy.y(u) = Pu.m_y;
            m_GAcopy.idNode(u) = icopy;
            icopy++;
        }

        // Generate random seed for edge weight generation
        srand (static_cast <unsigned> (time(0)));
    } // SetUp()

    virtual void TearDown() {
    } // TearDown()

    // Returns a random from 0 to 1.0
    double getRandomEdgeWeight(void) {
        return static_cast <float> (rand()) / static_cast <float> (RAND_MAX);
    }

    // Add directed edges between nodes in index order with random weights
    void addEdges() {
        node u, v;
        forall_nodes(v,m_G) {
            // Skip the first node
            if (m_G.firstNode() == v) {
                u = v;
                continue;
            }
            edge e = m_G.newEdge(u, v);
            // Generate: w = [0, 1.0]
            float w = getRandomEdgeWeight();
            m_GA.doubleWeight(e) = w;
            u = v;
        }
        forall_nodes(v,m_Gcopy) {
            // Skip the first node
            if (m_Gcopy.firstNode() == v) {
                u = v;
                continue;
            }
            edge e = m_Gcopy.newEdge(u, v);
            // Generate: w = [0, 1.0]
            float w = getRandomEdgeWeight();
            m_GAcopy.doubleWeight(e) = w;
            u = v;
        }
    }

protected:
    Graph m_G, m_Gcopy;
    GraphAttributes m_GA, m_GAcopy;

}; // class GraphsAreEquivalentTest

// Test for equivalence with empty graphs
TEST_F(GraphsAreEquivalentTest, EmptyGraphsAreEquivalent) {
    Graph G, Gcopy;
    GraphAttributes GA(G, DPP_GRAPH_ATTRIBUTES),
        GAcopy(Gcopy, DPP_GRAPH_ATTRIBUTES);

    ASSERT_EQ(0, G.numberOfNodes());
    ASSERT_EQ(0, Gcopy.numberOfNodes());
    ASSERT_EQ(0, G.numberOfEdges());
    ASSERT_EQ(0, Gcopy.numberOfEdges());

    EXPECT_EQ(1, dpp::graphsAreEquivalent(G, GA, Gcopy, GAcopy));
}

// Test for equivalence with empty graphs
TEST_F(GraphsAreEquivalentTest, SimpleGraphsNotEquivalent) {
    Graph G, Gcopy;
    GraphAttributes GA(G, DPP_GRAPH_ATTRIBUTES),
        GAcopy(Gcopy, DPP_GRAPH_ATTRIBUTES);

    node u = G.newNode();
    GA.x(u) = getRandomEdgeWeight();
    GA.x(u) = getRandomEdgeWeight();
    GA.idNode(u) = 1;

    ASSERT_EQ(1, G.numberOfNodes());
    ASSERT_EQ(0, Gcopy.numberOfNodes());
    ASSERT_EQ(0, G.numberOfEdges());
    ASSERT_EQ(0, Gcopy.numberOfEdges());

    EXPECT_EQ(0, dpp::graphsAreEquivalent(G, GA, Gcopy, GAcopy));
}

// Test for equivalence with node-only graphs
TEST_F(GraphsAreEquivalentTest, NodeOnlyGraphsAreEquivalent) {
    int expectedSize = nodes.size();
    ASSERT_EQ(expectedSize, m_G.numberOfNodes());
    ASSERT_EQ(expectedSize, m_Gcopy.numberOfNodes());

    EXPECT_EQ(1, dpp::graphsAreEquivalent(m_G, m_GA, m_Gcopy, m_GAcopy));
}

// Test for equivalence with full graphs
TEST_F(GraphsAreEquivalentTest, GraphsAreEquivalent) {
    int expectedSize = nodes.size();
    ASSERT_EQ(expectedSize, m_G.numberOfNodes());
    ASSERT_EQ(expectedSize, m_Gcopy.numberOfNodes());

    addEdges();
    ASSERT_EQ(m_G.numberOfNodes() - 1, m_G.numberOfEdges());
    ASSERT_EQ(m_G.numberOfNodes() - 1, m_Gcopy.numberOfEdges());

    EXPECT_EQ(1, dpp::graphsAreEquivalent(m_G, m_GA, m_Gcopy, m_GAcopy));
}

// --------------------- copyGraph() -------------------
class GraphCopyTest : public Test {
public:
    GraphCopyTest()
        : m_G(),
          m_GA (m_G, DPP_GRAPH_ATTRIBUTES ),
          m_Gcopy(),
          m_GAcopy (m_Gcopy, DPP_GRAPH_ATTRIBUTES )
    { }

    virtual ~GraphCopyTest() { }

    virtual void SetUp() {
        // Add nodes
        int i = 1;
        ListConstIterator<DPoint> iter;
        for ( iter = nodes.begin(); iter != nodes.end(); iter++ ) {
            DPoint Pu = *iter;
            node u = m_G.newNode();
            m_GA.x(u) = Pu.m_x;
            m_GA.y(u) = Pu.m_y;
            m_GA.idNode(u) = i;
            i++;
        }
        // Generate random seed for edge weight generation
        srand (static_cast <unsigned> (time(0)));
    } // SetUp()

    virtual void TearDown() {
    } // TearDown()

    // Add directed edges between nodes in index order with random weights
    void addEdges() {
        node u, v;
        forall_nodes(v,m_G) {
            // Skip the first node
            if (m_G.firstNode() == v) {
                u = v;
                continue;
            }
            edge e = m_G.newEdge(u, v);
            float w = getRandomEdgeWeight();
            m_GA.doubleWeight(e) = w;
        }
    }

    // Returns a random from 0 to 1.0
    double getRandomEdgeWeight(void) {
        return static_cast <float> (rand()) / static_cast <float> (RAND_MAX);
    }

protected:
    Graph m_G, m_Gcopy;
    GraphAttributes m_GA, m_GAcopy;

}; // class GraphCopyTest

TEST_F(GraphCopyTest, CopiesNodesOnly) {
    ASSERT_GT(m_G.numberOfNodes(), 0);
    ASSERT_EQ(m_G.numberOfEdges(), 0);
    ASSERT_EQ(m_Gcopy.numberOfNodes(), 0);
    ASSERT_EQ(m_Gcopy.numberOfEdges(), 0);

    int n = dpp::copyGraph(m_G, m_GA, m_Gcopy, m_GAcopy);

    EXPECT_EQ(m_G.numberOfNodes(), n);

    EXPECT_EQ(true, dpp::graphsAreEquivalent(m_G, m_GA, m_Gcopy, m_GAcopy));
}

TEST_F(GraphCopyTest, ClearsExistingNodes) {
    ASSERT_GT(m_G.numberOfNodes(), 0);
    ASSERT_EQ(m_G.numberOfEdges(), 0);
    ASSERT_EQ(m_Gcopy.numberOfNodes(), 0);
    ASSERT_EQ(m_Gcopy.numberOfEdges(), 0);

    node u = m_Gcopy.newNode();
    m_GAcopy.x(u) = 0.5;
    m_GAcopy.y(u) = 0.77;
    m_GAcopy.idNode(u) = 1;

    ASSERT_EQ(1, m_Gcopy.numberOfNodes());

    int n = dpp::copyGraph(m_G, m_GA, m_Gcopy, m_GAcopy);

    EXPECT_EQ(m_G.numberOfNodes(), n);

    EXPECT_EQ(true, dpp::graphsAreEquivalent(m_G, m_GA, m_Gcopy, m_GAcopy));
}
/*
TEST_F(GraphCopyTest, CopiesNodesAndEdges) {
    ASSERT_GT(m_G.numberOfNodes(), 0);
    ASSERT_EQ(m_G.numberOfEdges(), 0);

    addEdges();
    ASSERT_EQ(m_G.numberOfNodes() - 1, m_G.numberOfEdges());

    int n = dpp::copyGraph(m_G, m_GA, Gcopy, GAcopy);

    EXPECT_EQ(m_G.numberOfNodes(), n);

    EXPECT_EQ(true, dpp::graphsAreEquivalent(m_G, m_GA, m_Gcopy, m_GAcopy));
}
*/

// --------------------- Line2d -------------------
// Tests for constructor
TEST(Line2dTest, ConstructsWithDLine) {
    DLine dl(DPoint(0,0), DPoint(1,0));
    dpp::Line2d line(dl);

    EXPECT_DOUBLE_EQ(0, line.m_a);
    EXPECT_DOUBLE_EQ(1, line.m_b);
    EXPECT_DOUBLE_EQ(0, line.m_c);
    EXPECT_DOUBLE_EQ(dl.slope(), line.slope());
}

TEST(Line2dTest, ConstructsWithDSegment) {
    DSegment s(DPoint(0,0), DPoint(1,0));
    dpp::Line2d line(s);

    EXPECT_DOUBLE_EQ(0, line.m_a);
    EXPECT_DOUBLE_EQ(1, line.m_b);
    EXPECT_DOUBLE_EQ(0, line.m_c);
    EXPECT_DOUBLE_EQ(s.slope(), s.slope());
}

TEST(Line2dTest, ConstructsVerticalLine) {
    DLine dl(DPoint(2,0), DPoint(2,1));
    dpp::Line2d line(dl);

    EXPECT_DOUBLE_EQ(2, line.m_a);
    EXPECT_DOUBLE_EQ(1, line.m_b);
    EXPECT_DOUBLE_EQ(std::numeric_limits<double>::max(), line.m_c);
    EXPECT_DOUBLE_EQ(dl.slope(), line.slope());
    EXPECT_TRUE(line.isVertical());
}

// Tests for equality operators
TEST(Line2dEquality, HorizontalLinesEqual) {
    DLine dl(DPoint(-1,8), DPoint(5,8));
    DLine dl2(DPoint(-3,8), DPoint(1,8));
    dpp::Line2d line(dl);
    dpp::Line2d line2(dl2);

    ASSERT_TRUE(dl != dl2); // ensure Line2d == is different than DLine
    EXPECT_TRUE(line == line2);
}

TEST(Line2dEquality, HorizontalLinesNotEqual) {
    DLine dl(DPoint(-1,8), DPoint(5,8));
    DLine dl2(DPoint(-3,-8), DPoint(1,-8));
    dpp::Line2d line(dl);
    dpp::Line2d line2(dl2);

    ASSERT_TRUE(dl != dl2);
    EXPECT_TRUE(line != line2);
}

TEST(Line2dEquality, VerticalLinesEqual) {
    DLine dl(DPoint(-1,3), DPoint(-1,5));
    DLine dl2(DPoint(-1,-50), DPoint(-1,10));
    dpp::Line2d line(dl);
    dpp::Line2d line2(dl2);

    ASSERT_TRUE(dl != dl2);
    EXPECT_TRUE(line == line2);
}

TEST(Line2dEquality, VerticalLinesNotEqual) {
    DLine dl(DPoint(-1,3), DPoint(-1,5));
    DLine dl2(DPoint(-50,-50), DPoint(-50,10));
    dpp::Line2d line(dl);
    dpp::Line2d line2(dl2);

    ASSERT_TRUE(dl != dl2);
    EXPECT_TRUE(line != line2);
}

TEST(Line2dEquality, RegularLinesEqual) {
    DLine dl(DPoint(0,0), DPoint(1,1));
    DLine dl2(DPoint(-3,-3), DPoint(0,0));
    dpp::Line2d line(dl);
    dpp::Line2d line2(dl);
    ASSERT_TRUE(dl != dl2);
    EXPECT_TRUE(line == line2);
}

TEST(Line2dEquality, RegularLinesNotEqual) {
    DLine dl(DPoint(0,0), DPoint(1,1));
    DLine dl2(DPoint(-3,-3), DPoint(-3,0));
    dpp::Line2d line(dl);
    dpp::Line2d line2(dl2);
    ASSERT_TRUE(dl != dl2);
    EXPECT_TRUE(line != line2);
}

// Test for copy constructor
TEST(Line2dCopyTest, CopyConstructor) {
    DLine dl(DPoint(0,0), DPoint(-1, -2));
    dpp::Line2d line(dl);
    dpp::Line2d line2(line);

    EXPECT_TRUE(line == line2);
}

// Test for length()
TEST(Line2dTest, InfiniteLength) {
    DLine dl(DPoint(0,0), DPoint(1,1));
    dpp::Line2d line(dl);

    EXPECT_TRUE(line.length() == std::numeric_limits<double>::max());
}

// Tests for angle()
TEST(Line2dAngleTest, AngleOfHorizontalLine) {
    DLine dl(DPoint(0,0), DPoint(1,0));
    dpp::Line2d line(dl);
    double expectedOutput = 0;

    EXPECT_DOUBLE_EQ(expectedOutput, line.angle());
}

TEST(Line2dAngleTest, AngleOfVerticalLine) {
    DLine dl(DPoint(-1,5), DPoint(-1,8));
    dpp::Line2d line(dl);
    double expectedOutput = dpp::degToRad(90);

    EXPECT_DOUBLE_EQ(expectedOutput, line.angle());
}

TEST(Line2dAngleTest, AngleOfObtuseLine) {
    DLine dl(DPoint(-1,0), DPoint(-3,1));
    dpp::Line2d line(dl);
    double expectedOutput = atan2(dl.dy(), dl.dx());

    EXPECT_DOUBLE_EQ(expectedOutput, line.angle());
}

TEST(Line2dAngleTest, AngleOfLineWrapsAtPi) {
    DLine dl(DPoint(0,0), DPoint(-1, 0));
    dpp::Line2d line(dl);
    double expectedOutput = 0;

    EXPECT_DOUBLE_EQ(expectedOutput, line.angle());
}

TEST(Line2dAngleTest, AngleOfLineWrapsPastPi) {
    DLine dl(DPoint(0,0), DPoint(0,-1));
    dpp::Line2d line(dl);
    double expectedOutput = dpp::degToRad(90);

    EXPECT_DOUBLE_EQ(expectedOutput, line.angle());
}

// Tests for contains()
TEST(Line2dContainsTest, ContainsSegmentEndpoints) {
    DLine dl(DPoint(-5,3), DPoint(0,3));
    dpp::Line2d line(dl);
    DPoint p(-3,3);

    ASSERT_TRUE(dl.contains(dl.start()));
    ASSERT_TRUE(dl.contains(dl.end()));

    EXPECT_TRUE(line.contains(dl.start()));
    EXPECT_TRUE(line.contains(dl.end()));
    EXPECT_TRUE(line.contains(p));
}

TEST(Line2dContainsTest, ContainsPastSegmentEndpoints) {
    DLine dl(DPoint(0,0), DPoint(-1,-1));
    DPoint p1(-2,-2);
    DPoint p2(5,5);
    DPoint p3(0.5,0.5);
    dpp::Line2d line(dl);

    ASSERT_FALSE(dl.contains(p1));
    ASSERT_FALSE(dl.contains(p2));

    EXPECT_TRUE(line.contains(p1));
    EXPECT_TRUE(line.contains(p2));
    EXPECT_TRUE(line.contains(p3));
}

TEST(Line2dContainsTest, DoesNotContainPoint) {
    DLine dl(DPoint(0,0), DPoint(1,0));
    DPoint p(-1,-1);
    dpp::Line2d line(dl);

    ASSERT_FALSE(dl.contains(p));
    EXPECT_FALSE(line.contains(p));
}

TEST(Line2dContainsTest, VerticalLineContains) {
    DLine dl(DPoint(0,0), DPoint(0,-1));
    DPoint p(0,10);
    dpp::Line2d line(dl);

    ASSERT_FALSE(dl.contains(p));
    EXPECT_TRUE(line.contains(p));
}

TEST(Line2dContainsTest, VerticalLineDoesNotContain) {
    DLine dl(DPoint(0,0), DPoint(0,-1));
    DPoint p(1,0);
    dpp::Line2d line(dl);

    ASSERT_FALSE(dl.contains(p));
    EXPECT_FALSE(line.contains(p));
}

// Tests for translatePolar()
TEST(Line2dTranslateTest, TranslateHorizontalLineUp) {
    dpp::Line2d line(DLine(DPoint(-1,5), DPoint(5,5)));
    dpp::Line2d originalLine(line);
    DPoint p(10,8);
    double d = 3;
    double theta = dpp::degToRad(90);

    ASSERT_FALSE(line.contains(p));
    line.translatePolar(d, theta);
    ASSERT_TRUE(line != originalLine);
    EXPECT_TRUE(line.contains(p));
}

TEST(Line2dTranslateTest, TranslateHorizontalLineDown) {
    dpp::Line2d line(DLine(DPoint(-1,5), DPoint(5,5)));
    dpp::Line2d originalLine(line);
    DPoint p(10,2);
    double d = 3;
    double theta = dpp::degToRad(270);

    ASSERT_FALSE(line.contains(p));
    line.translatePolar(d, theta);
    ASSERT_TRUE(line != originalLine);
    EXPECT_TRUE(line.contains(p));
}

TEST(Line2dTranslateTest, TranslateVerticalLineRight) {
    dpp::Line2d line(DLine(DPoint(5,0), DPoint(5,7)));
    dpp::Line2d originalLine(line);
    DPoint p(7,59);
    double d = 2;
    double theta = dpp::degToRad(0);

    ASSERT_FALSE(line.contains(p));
    line.translatePolar(d, theta);
    ASSERT_TRUE(line != originalLine);
    EXPECT_TRUE(line.contains(p));
}

TEST(Line2dTranslateTest, TranslateVerticalLineLeft) {
    dpp::Line2d line(DLine(DPoint(5,0), DPoint(5,7)));
    dpp::Line2d originalLine(line);
    DPoint p(-3,-5);
    double d = 8;
    double theta = dpp::degToRad(180);

    ASSERT_FALSE(line.contains(p));
    line.translatePolar(d, theta);
    ASSERT_TRUE(line != originalLine);
    EXPECT_TRUE(line.contains(p));
}

TEST(Line2dTranslateTest, TranslateRegularLineRight) {
    dpp::Line2d line(DLine(DPoint(-1,1), DPoint(10,-7)));
    dpp::Line2d originalLine(line);
    DPoint p(1.058600941862662,
        3.830576295061160);
    double d = 3.5;
    double theta = dpp::wrapAngle(line.angle() - M_PI/2);

    ASSERT_FALSE(line.contains(p));
    //std::cout << "Before: " << line << " at " << dpp::radToDeg(line.angle()) << "." << std::endl;
    line.translatePolar(d, theta);
    //std::cout << "After: " << line << std::endl;
    ASSERT_TRUE(line != originalLine);
    EXPECT_TRUE(line.contains(p));
}

TEST(Line2dTranslateTest, TranslateRegularLineLeft) {
    dpp::Line2d line(DLine(DPoint(-1,1), DPoint(10,-7)));
    dpp::Line2d originalLine(line);
    DPoint p(-3.176235281397672,
        -1.992323511921797);
    double d = 3.7;
    double theta = dpp::wrapAngle(line.angle() + M_PI/2);

    ASSERT_FALSE(line.contains(p));
    line.translatePolar(d, theta);
    ASSERT_TRUE(line != originalLine);
    EXPECT_TRUE(line.contains(p));
}

TEST(Line2dTranslateTest, TranslateLineNonPerpendicular) {
    // Test regular
    {
        dpp::Line2d line(DLine(DPoint(-10,0),DPoint(5,0)));
        dpp::Line2d originalLine(line);
        DPoint p(-9.292893218813452,
            0.707106781186547);
        double d = 1;
        double theta = dpp::degToRad(45);
        ASSERT_FALSE(line.contains(p));
        line.translatePolar(d, theta);
        ASSERT_TRUE(line != originalLine);
        EXPECT_TRUE(line.contains(p));
    }

    // Test vertical
    {
        dpp::Line2d line(DLine(DPoint(0,5),DPoint(0,-3)));
        dpp::Line2d originalLine(line);
        DPoint p(0.707106781186548,0);
        double d = 1;
        double theta = dpp::degToRad(45);
        ASSERT_FALSE(line.contains(p));
        line.translatePolar(d, theta);
        ASSERT_TRUE(line != originalLine);
        EXPECT_TRUE(line.contains(p));
    }
}

TEST(Line2dTranslateTest, TranslateLineParallelDoesNothing) {
    // Test regular
    {
        dpp::Line2d line(DLine(DPoint(-10,0),DPoint(5,0)));
        dpp::Line2d originalLine(line);
        double d = 10;
        double theta = dpp::degToRad(0);
        //std::cout << "Before: " << line << " at " << dpp::radToDeg(line.angle()) << "." << std::endl;
        line.translatePolar(d, theta);
        //std::cout << "After: " << line << std::endl;
        EXPECT_TRUE(line == originalLine);
    }

    // Test vertical
    {
        dpp::Line2d line(DLine(DPoint(0,5),DPoint(0,-3)));
        dpp::Line2d originalLine(line);
        double d = 10;
        double theta = dpp::degToRad(90);
        //std::cout << "Before: " << line << " at " << dpp::radToDeg(line.angle()) << "." << std::endl;
        line.translatePolar(d, theta);
        //std::cout << "After: " << line << std::endl;
        EXPECT_TRUE(line == originalLine);
    }
}

TEST(Line2dTranslateTest, TranslateLineZeroDistance) {
    // Test regular
    {
        dpp::Line2d line(DLine(DPoint(-10,0),DPoint(5,0)));
        dpp::Line2d originalLine(line);
        double d = 0;
        double theta = dpp::degToRad(90);
        line.translatePolar(d, theta);
        EXPECT_TRUE(line == originalLine);
    }

    // Test vertical
    {
        dpp::Line2d line(DLine(DPoint(0,5),DPoint(0,-3)));
        dpp::Line2d originalLine(line);
        double d = 0;
        double theta = dpp::degToRad(0);
        line.translatePolar(d, theta);
        EXPECT_TRUE(line == originalLine);
    }
}

// Tests for intersction() with lines
TEST(Line2dIntersectionTest, NoIntersectionVertical) {
    dpp::Line2d line(DLine(DPoint(-2,-10), DPoint(-2,14)));
    dpp::Line2d line2(DLine(DPoint(-5,10), DPoint(-5,14)));

    DPoint p;
    EXPECT_FALSE(line.intersection(line2,p));
}

TEST(Line2dIntersectionTest, NoIntersectionHorizontal) {
    dpp::Line2d line(DLine(DPoint(-2,-10), DPoint(10,-10)));
    dpp::Line2d line2(DLine(DPoint(2,10), DPoint(10,10)));

    DPoint p;
    EXPECT_FALSE(line.intersection(line2,p));
}

TEST(Line2dIntersectionTest, NoIntersectionIdentical) {
    // Horizontal
    {
        dpp::Line2d line(DLine(DPoint(-2,10), DPoint(10,10)));
        dpp::Line2d line2(DLine(DPoint(2,10), DPoint(10,10)));

        DPoint p;
        EXPECT_FALSE(line.intersection(line2,p));
    }
    // Vertical
    {
        dpp::Line2d line(DLine(DPoint(-5,-10), DPoint(-5,14)));
        dpp::Line2d line2(DLine(DPoint(-5,10), DPoint(-5,14)));

        DPoint p;
        EXPECT_FALSE(line.intersection(line2,p));
    }
}

TEST(Line2dIntersectionTest, HasIntersection) {
    // Horizontal and vertical
    {
        dpp::Line2d line(DLine(DPoint(-2,10), DPoint(10,10)));
        dpp::Line2d line2(DLine(DPoint(0,10), DPoint(0,15)));

        DPoint expectedP(0,10);
        DPoint actualP;
        EXPECT_TRUE(line.intersection(line2,actualP));
        EXPECT_TRUE(expectedP == actualP);
    }
    // Horizontal and regular
    {
        dpp::Line2d line(DLine(DPoint(-2,0), DPoint(10,0)));
        dpp::Line2d line2(DLine(DPoint(0,0), DPoint(10,10)));

        DPoint expectedP(0,0);
        DPoint actualP;
        EXPECT_TRUE(line.intersection(line2,actualP));
        EXPECT_TRUE(expectedP == actualP);
    }
    // Vertical and regular
    {
        dpp::Line2d line(DLine(DPoint(0,10), DPoint(0,15)));
        dpp::Line2d line2(DLine(DPoint(-1,10), DPoint(-5,14)));

        DPoint expectedP(0,9);
        DPoint actualP;
        EXPECT_TRUE(line.intersection(line2,actualP));
        EXPECT_TRUE(expectedP == actualP);
    }
}

// Tests for intersction() with segments
TEST(Line2dIntersectionTest, NoSegmentIntersection) {
    // Horizontal segment
    {
        dpp::Line2d line(DLine(DPoint(0,10), DPoint(0,10)));
        DSegment s(DLine(DPoint(2,10), DPoint(10,10)));

        DPoint p;
        EXPECT_FALSE(line.intersection(s,p));
    }
    // Vertical segment
    {
        dpp::Line2d line(DLine(DPoint(0,10), DPoint(0,10)));
        DSegment s(DLine(DPoint(1,5), DPoint(1,10)));

        DPoint p;
        EXPECT_FALSE(line.intersection(s,p));
    }
}

TEST(Line2dIntersectionTest, NoSegmentIntersectionOnTopOf) {
    // Horizontal
    {
        dpp::Line2d line(DLine(DPoint(-2,10), DPoint(10,10)));
        DSegment s(DLine(DPoint(-5,10), DPoint(-3,10)));

        DPoint p;
        EXPECT_FALSE(line.intersection(s,p));
    }
    // Vertical
    {
        dpp::Line2d line(DLine(DPoint(-2,10), DPoint(-2,15)));
        DSegment s(DLine(DPoint(-2,3), DPoint(-2,10)));

        DPoint p;
        EXPECT_FALSE(line.intersection(s,p));
    }
}

TEST(Line2dIntersectionTest, HasSegmentIntersection) {
    // Horizontal
    {
        dpp::Line2d line(DLine(DPoint(0,0), DPoint(5,5)));
        DSegment s(DLine(DPoint(-5,0), DPoint(5,0)));

        DPoint expectedP(0,0);
        DPoint actualP;
        EXPECT_TRUE(line.intersection(s,actualP));
        EXPECT_TRUE(expectedP == actualP);
    }
    // Vertical
    {
        dpp::Line2d line(DLine(DPoint(0,0), DPoint(5,5)));
        DSegment s(DLine(DPoint(-2,-2), DPoint(-2,10)));

        DPoint expectedP(-2,-2);
        DPoint actualP;
        EXPECT_TRUE(line.intersection(s,actualP));
        EXPECT_TRUE(expectedP == actualP);
    }
    // Regular
    {
        dpp::Line2d line(DLine(DPoint(0,10), DPoint(0,15)));
        DSegment s(DLine(DPoint(-1,-1), DPoint(1,1)));

        DPoint expectedP(0,0);
        DPoint actualP;
        EXPECT_TRUE(line.intersection(s,actualP));
        EXPECT_TRUE(expectedP == actualP);
    }
}


// Copyright (c) 2022 CNES
//
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#pragma once
#include <Eigen/Core>
#include <Eigen/LU>
#include <cmath>
#include <functional>

#include "pyinterp/eigen.hpp"

namespace pyinterp::detail::math {

/// Known radial functions.
enum RadialBasisFunction : uint8_t {
  Cubic,
  Gaussian,
  InverseMultiquadric,
  Linear,
  Multiquadric,
  ThinPlate
};

//// A radial basis function (RBF) is a real-valued function φ whose value
/// depends only on the distance between the input and some fixed point,
/// either the origin, so that φ(x) = φ(║x║), or some other fixed point
/// c, called a center, so that φ(x) = φ(║x - c║). Any function φ that
/// satisfies the property φ(x) = φ(║x║) is a radial function.
template <typename T>
class RBF {
 public:
  /// Pointer to the Radial function used
  using PtrRadialBasisFunction =
      Matrix<T> (*)(const Eigen::Ref<const Matrix<T>>& r, const T);

  /// Default constructor
  ///
  /// @param epsilon Adjustable constant for gaussian or multiquadrics
  /// functions - defaults to approximate average distance between nodes
  /// (which is a good start).
  /// @param smooth Values greater than zero increase the smoothness of the
  /// approximation. 0 is for interpolation (default), the function will always
  /// go through the nodal points in this case.
  /// @param rbf The radial basis function, based on the radius, r, given by the
  /// norm (Euclidean distance)
  RBF(const T& epsilon, const T& smooth, const RadialBasisFunction rbf)
      : epsilon_(T(1) / epsilon), smooth_(smooth) {
    switch (rbf) {
      case RadialBasisFunction::Cubic:
        function_ = &RBF::cubic;
        break;
      case RadialBasisFunction::Gaussian:
        function_ = &RBF::gaussian;
        break;
      case RadialBasisFunction::InverseMultiquadric:
        function_ = &RBF::inverse_multiquadric;
        break;
      case RadialBasisFunction::Linear:
        function_ = &RBF::linear;
        break;
      case RadialBasisFunction::Multiquadric:
        function_ = &RBF::multiquadric;
        break;
      case RadialBasisFunction::ThinPlate:
        function_ = &RBF::thin_plate;
        break;
      default:
        throw std::invalid_argument("Radial function unknown: " +
                                    std::to_string(static_cast<int>(rbf)));
    }
  }

  /// Calculates the interpolated values
  ///
  /// @param xk Coordinates of the nodes
  /// @param yk Values of the nodes
  /// @param xi Coordinates to evaluate the interpolant at.
  /// @return interpolated values for each coordinates provided.
  [[nodiscard]] auto interpolate(const Eigen::Ref<const Matrix<T>>& xk,
                                 const Eigen::Ref<const Vector<T>>& yk,
                                 const Eigen::Ref<const Matrix<T>>& xi) const
      -> Vector<T> {
    // Matrix of distances between the coordinates provided.
    const auto r = RBF::distance_matrix(xk, xk);

    // Default epsilon to approximate average distance between nodes
    const auto epsilon =
        std::isnan(epsilon_) ? 1 / RBF<T>::average(r) : epsilon_;

    // TODO(fbriol)
    auto A = function_(r, epsilon);

    // Apply smoothing factor if needed
    if (smooth_) {
      A -= Matrix<T>::Identity(xk.cols(), xk.cols()) * smooth_;
    }

    return function_(distance_matrix(xk, xi), epsilon) *
           RBF<T>::solve_linear_system(A, yk);
  }

 private:
  /// Adjustable constant for gaussian or multiquadrics functions
  T epsilon_;

  /// Smooth factor
  T smooth_;

  /// Radial bassis function, based on the radius
  PtrRadialBasisFunction function_;

  // Calculates the distance average excluding the diagonal
  static auto average(const Eigen::Ref<const Matrix<T>>& distance) -> T {
    assert(distance.cols() == distance.rows());
    auto sum = T(0);
    auto n = distance.cols();
    for (Eigen::Index ix = 0; ix < distance.rows() - 1; ++ix) {
      for (Eigen::Index jx = ix + 1; jx < distance.cols(); ++jx) {
        sum += distance(ix, jx);
      }
    }
    return static_cast<T>(sum / (n * (n - 1) * 0.5));
  };

  /// Returns the distance between two points in a euclidean space
  static auto euclidean_distance(const Eigen::Ref<const Vector<T>>& x,
                                 const Eigen::Ref<const Vector<T>>& y) -> T {
    return std::sqrt((x - y).array().pow(2).sum());
  }

  /// Multiquadric
  static auto multiquadric(const Eigen::Ref<const Matrix<T>>& r,
                           const T epsilon) -> Matrix<T> {
    return ((epsilon * r).array().pow(2) + 1).sqrt();
  }

  /// Inverse multiquadric
  static auto inverse_multiquadric(const Eigen::Ref<const Matrix<T>>& r,
                                   const T epsilon) -> Matrix<T> {
    return 1.0 / multiquadric(r, epsilon).array();
  }

  /// Gauss
  static auto gaussian(const Eigen::Ref<const Matrix<T>>& r, const T epsilon)
      -> Matrix<T> {
    return (-(epsilon * r).array().pow(2)).exp();
  }

  /// Linear spline
  static auto linear(const Eigen::Ref<const Matrix<T>>& r, const T /*epsilon*/)
      -> Matrix<T> {
    return r;
  }

  /// Cubic spline
  static auto cubic(const Eigen::Ref<const Matrix<T>>& r, const T /*epsilon*/)
      -> Matrix<T> {
    return r.array().pow(3);
  }

  /// Thin plate spline
  static auto thin_plate(const Eigen::Ref<const Matrix<T>>& r,
                         const T /*epsilon*/) -> Matrix<T> {
    return (r.array() == 0).select(0, r.array().pow(2) * r.array().log());
  }

  /// Calculation of distances between the coordinates provided.
  static auto distance_matrix(const Eigen::Ref<const Matrix<T>>& xk,
                              const Eigen::Ref<const Matrix<T>>& xi)
      -> Matrix<T> {
    assert(xk.rows() == xi.rows());

    auto result = Matrix<T>(xi.cols(), xk.cols());

    for (Eigen::Index i0 = 0; i0 < xi.cols(); ++i0) {
      for (Eigen::Index i1 = 0; i1 < xk.cols(); ++i1) {
        result(i0, i1) = euclidean_distance(xi.col(i0), xk.col(i1));
      }
    }
    return result;
  }

  /// Resolution of the linear system
  static auto solve_linear_system(const Matrix<T>& A, const Vector<T>& di)
      -> Vector<T> {
    Eigen::FullPivLU<Matrix<T>> lu(A);
    return lu.solve(di);
  }
};

}  // namespace pyinterp::detail::math


/**
 * @file
 * @brief NPDE homework ElementMatrixComputation code
 * @author Janik Schüttler, edited by Oliver Rietmann
 * @date 03.03.2019
 * @copyright Developed at ETH Zurich
 */

#include "mylinearloadvector.h"

#include <lf/base/base.h>
#include <lf/geometry/geometry.h>
#include <lf/mesh/mesh.h>

#include <Eigen/Core>
#include <functional>

namespace ElementMatrixComputation {

namespace {

/* SAM_LISTING_BEGIN_1 */
Eigen::Vector4d computeLoadVector(
    const Eigen::MatrixXd &vertices,
    std::function<double(const Eigen::Vector2d &)> f) {
  // Number of nodes of the element: triangles = 3, rectangles = 4
  const int num_nodes = vertices.cols();
  // Vector for returning element vector
  Eigen::Vector4d elem_vec = Eigen::Vector4d::Zero();

  //====================

  // first two midpoints of edges of cell
  Eigen::Vector2d m1 = 0.5*(vertices.col(0) + vertices.col(1));
  Eigen::Vector2d m2 = 0.5*(vertices.col(1) + vertices.col(2));

  // == TRIANGLE ==
  if (num_nodes == 3) {
    // Third midpoint of edge
    Eigen::Vector2d m3 = 0.5*(vertices.col(2) + vertices.col(0));

    // Area
    double K = 0.5*( (vertices(0,1) - vertices(0,0)) * (vertices(1,2) - vertices(1,0)) -
                     (vertices(1,1) - vertices(1,0)) * (vertices(0,2) - vertices(0,0)));
    K /= num_nodes;

    elem_vec(0) = f(m1) + f(m3);
    elem_vec(1) = f(m1) + f(m2);
    elem_vec(2) = f(m2) + f(m3);
    elem_vec *= K/2.0;

    // == QUADRILITERA ==
    } else if (num_nodes == 4) {
    // Third and forth midpoints of edge
    Eigen::Vector2d m3 = 0.5*(vertices.col(2) + vertices.col(3));
    Eigen::Vector2d m4 = 0.5*(vertices.col(3) + vertices.col(0));

    // Area
    double K = ( vertices(0,1) - vertices(0,0) )*
               ( vertices(1,3) - vertices(1,0) );
    K /= num_nodes;

    elem_vec(0) = f(m1) + f(m4);
    elem_vec(1) = f(m1) + f(m2);
    elem_vec(2) = f(m2) + f(m3);
    elem_vec(3) = f(m3) + f(m4);
    elem_vec *= K/2.0;
    }

  //====================

  return elem_vec;
}
/* SAM_LISTING_END_1 */

}  // namespace

Eigen::Vector4d MyLinearLoadVector::Eval(const lf::mesh::Entity &cell) {
  // Topological type of the cell
  const lf::base::RefEl ref_el{cell.RefEl()};
  const lf::base::size_type num_nodes{ref_el.NumNodes()};

  // Obtain the vertex coordinates of the cell, which completely
  // describe its shape.
  const lf::geometry::Geometry *geo_ptr = cell.Geometry();

  // Matrix storing corner coordinates in its columns
  auto vertices = geo_ptr->Global(ref_el.NodeCoords());

  return computeLoadVector(vertices, f_);
}

}  // namespace ElementMatrixComputation


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

using namespace Eigen;
using namespace std;

int main(int, char**)
{
  cout.precision(3);
  Matrix4f A = MatrixXf::Random(4,4);
cout << "Here is a random 4x4 matrix:" << endl << A << endl;
HessenbergDecomposition<MatrixXf> hessOfA(A);
MatrixXf H = hessOfA.matrixH();
cout << "The Hessenberg matrix H is:" << endl << H << endl;
MatrixXf Q = hessOfA.matrixQ();
cout << "The orthogonal matrix Q is:" << endl << Q << endl;
cout << "Q H Q^T is:" << endl << Q * H * Q.transpose() << endl;

  return 0;
}


//
// Created by Daniel on 31.03.2015.
//
#include "stack.h"
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <sstream>
using namespace boost::algorithm;

bool checkParentheses(std::string str) {
    auto parentheses = stack<char>();
    for (auto c : str) {
        if (c == ')' || c == ']' || c == '}' || c == '>') {
            if (parentheses.isEmpty() || parentheses.pop() != c)
                return false;
        }
        if (c == '(') parentheses.push(')');
        if (c == '[') parentheses.push(']');
        if (c == '{') parentheses.push('}');
        if (c == '<') parentheses.push('>');
    }
    return parentheses.isEmpty();
}

std::string toBinary(int num) {
    return toBase(num, 2);
}

std::string toBase(int num, int base) {
    auto digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    auto digitsInBase = stack<char>();
    while (num > 0) {
        digitsInBase.push(digits[num % base]);
        num /= base;
    }

    auto res = std::string();

    while (!digitsInBase.isEmpty())
    {
        res += digitsInBase.pop();
    }

    return res;
}

std::string toPostfix(std::string str) {
    std::vector<std::string> tokens;
    split(tokens, str, boost::is_space(), token_compress_on);
    auto prec = [] (auto str) {
        if(str == "+" || str == "-")
            return 1;
        if(str == "*" || str == "/")
            return 2;
        return 0;
    };

    auto operators = stack<std::string>();

    std::stringstream res;

    for (auto token : tokens) {
        if(token == "(")
            operators.push(token);
        else if (token == ")") {
            auto op = operators.pop();
            while (op != "(") {
                res << op << " ";
                op = operators.pop();
            };
        }
        else if (token == "+" || token == "-" || token == "*" || token == "/") {
            while (!operators.isEmpty() && prec(token) <= prec(operators.peek()))
                res << operators.pop() << " ";
            operators.push(token);
        }
        else
            res << token << " ";
    }

    while (!operators.isEmpty())
        res << operators.pop() << " ";

    return res.str().erase(res.str().length() - 1);
}

int evalPostfix(std::string str) {
    std::vector<std::string> tokens;
    split(tokens, str, boost::is_space(), token_compress_on);
    auto evalStack = stack<int>();
    auto eval = [&](auto binOp) {
        auto right = evalStack.pop();
        auto left = evalStack.pop();
        evalStack.push(binOp(left, right));
    };

    for(auto token : tokens)
    {
        if(token == "+") eval([](auto x, auto y) { return x + y; });
        else if(token == "-") eval([](auto x, auto y) { return x - y; });
        else if(token == "*") eval([](auto x, auto y) { return x * y; });
        else if(token == "/") eval([](auto x, auto y) { return x / y; });
        else evalStack.push(boost::lexical_cast<int>(token));
    }

    return evalStack.pop();
}


// Boost.Geometry (aka GGL, Generic Geometry Library)
// QuickBook Example

// Copyright (c) 2014 Barend Gehrels, Amsterdam, the Netherlands.

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

//[buffer_point_square
//` Shows how the point_square strategy can be used as a PointStrategy to create square buffers where the original point lies in the center

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/geometries.hpp>
/*<-*/ #include "../examples_utils/create_svg_buffer.hpp" /*->*/

int main()
{
    typedef boost::geometry::model::d2::point_xy<double> point;
    typedef boost::geometry::model::polygon<point> polygon;

    // Declare the point_square strategy
    boost::geometry::strategy::buffer::point_square point_strategy;

    // Declare other strategies
    boost::geometry::strategy::buffer::distance_symmetric<double> distance_strategy(0.5);
    boost::geometry::strategy::buffer::join_round join_strategy;
    boost::geometry::strategy::buffer::end_round end_strategy;
    boost::geometry::strategy::buffer::side_straight side_strategy;

    // Declare/fill of a multi point
    boost::geometry::model::multi_point<point> mp;
    boost::geometry::read_wkt("MULTIPOINT((3 3),(3 4),(4 4),(7 3))", mp);

    // Create the buffer of a multi point
    boost::geometry::model::multi_polygon<polygon> result;
    boost::geometry::buffer(mp, result,
                distance_strategy, side_strategy,
                join_strategy, end_strategy, point_strategy);
    /*<-*/ create_svg_buffer("buffer_point_square.svg", mp, result); /*->*/

    return 0;
}

//]


#include "problemes.h"
#include "arithmetique.h"
#include "premiers.h"

#include <fstream>
#include <boost/algorithm/string.hpp>

typedef unsigned long long nombre;
typedef std::vector<nombre> vecteur;

ENREGISTRER_PROBLEME(108, "Diophantine reciprocals I") {
    // In the following equation x, y, and n are positive integers.
    //
    //                                          1/x + 1/y = 1/n
    //
    // For n = 4 there are exactly three distinct solutions:
    //
    //      1/5 + 1/20 = 1/4
    //      1/6 + 1/12 = 1/4
    //      1/8 + 1/8 = 1/4
    //
    // What is the least value of n for which the number of distinct solutions exceeds one-thousand?
    //
    // NOTE: This problem is an easier version of Problem 110; it is strongly advised that you solve this one first.
    vecteur premiers;
    premiers::crible23<nombre>(1000000, std::back_inserter(premiers));

    nombre resultat = 0;
    for (nombre n = 2;; ++n) {
        std::map<nombre, nombre> decomposition;
        arithmetique::decomposition(n, premiers, decomposition);
        nombre solutions = 1;
        for (const auto &d: decomposition) {
            solutions *= (2 * d.second + 1);
        }
        solutions = (solutions + 1) / 2;
        if (solutions > 1000) {
            resultat = n;
            break;
        }
    }

    return std::to_string(resultat);
}


#include <dlib/matrix.h>
#include <dlib/optimization.h>
#include <iostream>
#include <vector>

using namespace std;

using column_vector = dlib::matrix<double, 0, 1>;

double f(const column_vector& col) {
    assert(col.nr() == 2);

    const double x1 = col(0);
    const double x2 = col(1);

    const double ret = -(x1 - 2) * (x1 - 2) - (x2 - 3) * (x2 - 3) + 5;

    printf("func called at x1 = %f, x2 = %f, f = %f\n", x1, x2, ret);

    return ret;
}

int main() {
    column_vector x = {0, 0};

    double ret = dlib::find_max_bobyqa(
        f, x, 5, dlib::uniform_matrix<double>(2, 1, -100.0),
        dlib::uniform_matrix<double>(2, 1, 100.0), 10, 1.E-6, 100);

    cout << x << endl;
    cout << ret << endl;

    return 0;
}


#include "BoostOdeint.hpp"

#include <Eigen/Core>

#include <tuple>
#include <iostream>

using namespace sodes::odeint;
using namespace Eigen;

// System to be solved
ArrayXd lorenz(const Ref<ArrayXd> x, Ref<ArrayXd> dxdt, const double &t)
{
    double sigma = 10.0;
    double R = 28.0;
    double b = 8.0 / 3.0;

    dxdt[0] = sigma * (x[1] - x[0]);
    dxdt[1] = R * x[0] - x[1] - x[0] * x[2];
    dxdt[2] = -b * x[2] + x[0] * x[1];

    return dxdt;
}

int main (){
    // Defining the state vector with initial conditions
    int num_of_states = 3;
    ArrayXd X(num_of_states);
    X << 0., 1., 0.1;

    // Set time range and increment
    auto t_init = 0.0;
    auto t_final = 10.0;
    auto t_span = std::make_tuple(t_init, t_final);
    auto dt = 0.01;

    // Containers to store solution
    std::vector<ArrayXd> x_sol;
    std::vector<double> times;
    std::tie(times, x_sol) = solve_ivp_const(lorenz, t_span, dt, X, "runge_kutta_cash_karp54");

    // Displaying result on terminal
    auto steps = times.size();
    for (size_t i = 0; i < steps; i++)
    {
        std::cout << times[i] << '\t' << x_sol[i][0] << '\t' << x_sol[i][1] << '\t' << x_sol[i][2] << '\n';
    }
}


// TestRandom101.cpp
//
// Basic functionality to show how Random works
//
// (C) Datasim Education BV 2010-2011
//

#include <boost/random.hpp> // Convenience header file
#include <iostream>
#include <ctime>			// std::time
#include <boost/Random/detail/const_mod.hpp> // LCG class

using namespace std;

template<class PRNG, class Dist>
inline boost::variate_generator<PRNG&, Dist> make_gen(PRNG & rng, Dist d)
{
  return boost::variate_generator<PRNG&, Dist>(rng, d);
}

int main()
{

	// Get random number generator
	boost::mt19937 rng;
	rng.seed(static_cast<unsigned int> (std::time(0)));

	boost::triangle_distribution<> tri(0.0, 0.5, 1.0);

	//boost::lagged_fibonacci607 rng;
	rng.seed(static_cast<unsigned int> (std::time(0)));
	boost::variate_generator<boost::mt19937&, boost::triangle_distribution<> >
							triRng(rng, tri);
//	boost::variate_generator<boost::lagged_fibonacci607&, boost::triangle_distribution<> >
	//						triRng(rng, tri);

	for (int n = 0; n <=100; ++n)
	{
		cout << triRng() << ",";
	}

	return 0;

}

Level_8/V. An Introduction to the Boost C++ Libraries/04a - Random/TestRandom101.cpp