// Copyright (C) 2011  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.

#include <dlib/matrix.h>
#include <sstream>
#include <string>
#include <cstdlib>
#include <ctime>
#include <vector>
#include "../stl_checked.h"
#include "../array.h"
#include "../rand.h"
#include "checkerboard.h"
#include <dlib/statistics.h>

#include "tester.h"
#include <dlib/svm_threaded.h>

namespace
{

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

    logger dlog("test.probabilistic");

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

    class test_probabilistic : public tester
    {
    public:
        test_probabilistic (
        ) :
            tester ("test_probabilistic",
                    "Runs tests on the probabilistic trainer adapter.")
        {}

        void perform_test (
        )
        {
            print_spinner();

            typedef double scalar_type;
            typedef matrix<scalar_type,2,1> sample_type;

            std::vector<sample_type> x;
            std::vector<matrix<double,0,1> > x_linearized;
            std::vector<scalar_type> y;

            get_checkerboard_problem(x,y, 1000, 2);

            random_subset_selector<sample_type> rx;
            random_subset_selector<scalar_type> ry;
            rx.set_max_size(x.size());
            ry.set_max_size(x.size());

            dlog << LINFO << "pos labels: "<< sum(mat(y) == +1);
            dlog << LINFO << "neg labels: "<< sum(mat(y) == -1);

            for (unsigned long i = 0; i < x.size(); ++i)
            {
                rx.add(x[i]);
                ry.add(y[i]);
            }

            const scalar_type gamma = 2.0;

            typedef radial_basis_kernel<sample_type> kernel_type;

            krr_trainer<kernel_type> krr_trainer;
            krr_trainer.use_classification_loss_for_loo_cv();
            krr_trainer.set_kernel(kernel_type(gamma));
            krr_trainer.set_basis(randomly_subsample(x, 100));
            probabilistic_decision_function<kernel_type> df;

            dlog << LINFO << "cross validation: " << cross_validate_trainer(krr_trainer, rx,ry, 4);
            print_spinner();

            running_stats<scalar_type> rs_pos, rs_neg;

            print_spinner();
            df = probabilistic(krr_trainer,3).train(x, y);
            for (unsigned long i = 0; i < x.size(); ++i)
            {
                if (y[i] > 0)
                    rs_pos.add(df(x[i]));
                else
                    rs_neg.add(df(x[i]));
            }
            dlog << LINFO << "rs_pos.mean(): "<< rs_pos.mean();
            dlog << LINFO << "rs_neg.mean(): "<< rs_neg.mean();
            DLIB_TEST_MSG(rs_pos.mean() > 0.95, rs_pos.mean());
            DLIB_TEST_MSG(rs_neg.mean() < 0.05, rs_neg.mean());
            rs_pos.clear();
            rs_neg.clear();

            print_spinner();
            df = probabilistic(krr_trainer,3).train(rx, ry);
            for (unsigned long i = 0; i < x.size(); ++i)
            {
                if (y[i] > 0)
                    rs_pos.add(df(x[i]));
                else
                    rs_neg.add(df(x[i]));
            }
            dlog << LINFO << "rs_pos.mean(): "<< rs_pos.mean();
            dlog << LINFO << "rs_neg.mean(): "<< rs_neg.mean();
            DLIB_TEST_MSG(rs_pos.mean() > 0.95, rs_pos.mean());
            DLIB_TEST_MSG(rs_neg.mean() < 0.05, rs_neg.mean());
            rs_pos.clear();
            rs_neg.clear();

        }
    } a;

}


/*
 * Copyright (c) 2006-2012 Nicholas Devenish
 *
 * 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.
 *
 */

#ifndef RANDOM_H
#define RANDOM_H

#include <iostream>
#include <ctime> // For time()

#include "random.h"

#include <boost/random.hpp>
#include <boost/random/uniform_real.hpp>
#include <boost/random/normal_distribution.hpp>
#include <boost/random/variate_generator.hpp>

using boost::mt11213b;
using std::cout;
using std::endl;

static mt11213b rng;
static boost::uniform_real<> uni_r(0,1);
static boost::normal_distribution<> norm_r(0,1);

static boost::variate_generator<mt11213b&, boost::uniform_real<> > uni(rng, uni_r);
static boost::variate_generator<mt11213b&, boost::normal_distribution<> > norm(rng, norm_r);

// Initialise the random number generator
//initrn initirandomm();
namespace nsl{

void seedrand()
{
	cout << "Initialising random number generator..." << endl;
	rng.seed(static_cast<unsigned> (std::time(0)));
}

long double rand_uniform()
{
	return uni();
}

long double rand_normal()
{
	return norm();
}

}

#endif


#ifndef CX_SVD_HPP
#define CX_SVD_HPP

#include "print.hpp"
//#include "cx_modify.hpp"
#include "sub.hpp"
#include "../tools.hpp"
// #include "cx_multiply.hpp"
// #include "cx_special.hpp"

#include <mkl.h>

#include <boost/timer/timer.hpp>
#include <boost/chrono.hpp>

#include <vector>
#include <utility>
#include <complex>

// inline bool comparator ( const max_pair& l, const max_pair& r) { return l.first > r.first; }

namespace dqmc {
    namespace la {

	inline perm_mat cx_find_col_perm(cx_mat&__restrict in,
					 cx_mat&__restrict out) {
	    using namespace std;
	    using namespace Eigen;
	    mat_t::Index min_col;
	    ivec col_ids(in.cols());
	    for (int i = 0; i < in.rows(); ++i) {
		in.col(i).cwiseAbs().minCoeff(&min_col);
		cout << min_col << endl;
		col_ids[i] = int(min_col) - 1;
	    }
	    // for (std::vector<max_pair>::iterator it=min_indices.begin();
	    // 	 it != min_indices.end(); ++it) {
	    // 	col_ids((*it).second) = std::distance(min_indices.begin(), it);
	    // 	// cout << row_ids((*it).second) <<  " ";
	    // }
	    // // cout << endl;
	    perm_mat col_perm(col_ids);

	    out = in * col_perm.transpose();
	    return col_perm;
	}

	inline perm_mat cx_row_sort(cx_mat&__restrict in, cx_mat&__restrict out) {
	    using namespace std;
	    using namespace Eigen;
	    vector<int> max_index(in.rows());
	    vector<max_pair> max_indices;
	    vec max_vals(in.rows());
	    mat A = in.cwiseAbs().real().cast<double>();

	    for(int i = 0; i < in.rows(); ++i) {
		max_vals(i) = A.cwiseAbs().real().row(i).maxCoeff( &max_index[i] );
		max_indices.push_back(max_pair(max_vals(i), i));
	    }
	    std::sort(max_indices.begin(), max_indices.end(), comparator);

	    ivec row_ids(in.rows());
	    for (std::vector<max_pair>::iterator it=max_indices.begin(); it != max_indices.end(); ++it) {
		row_ids((*it).second) = std::distance(max_indices.begin(), it);
		// cout << row_ids((*it).second) <<  " ";
	    }
	    // cout << endl;
	    perm_mat row_perm(row_ids);

	    out = row_perm * in;
	    return row_perm;
	}

	inline perm_mat cx_col_sort(cx_mat&__restrict in,
				    cx_mat&__restrict out) {
	    using namespace std;
	    using namespace Eigen;
	    vector<int> max_index(in.cols());
	    vector<max_pair> max_indices;
	    vec max_vals(in.cols());
	    mat A = in.cwiseAbs().real().cast<double>();
	    for(int i = 0; i < in.cols(); ++i) {
		max_vals(i) = A.col(i).maxCoeff( &max_index[i] );
		max_indices.push_back(max_pair(max_vals(i), i));
	    }
	    std::sort(max_indices.begin(), max_indices.end(), comparator);

	    ivec col_ids(in.cols());
	    for (std::vector<max_pair>::iterator it=max_indices.begin();
		 it != max_indices.end(); ++it) {
		col_ids((*it).second) = std::distance(max_indices.begin(), it);
		// cout << row_ids((*it).second) <<  " ";
	    }
	    // cout << endl;
	    perm_mat col_perm(col_ids);

	    out = in * col_perm;
	    return col_perm;
	}

	inline void decompose_svd(cx_mat&__restrict__ in,
				  cx_mat&__restrict__ U,
				  vec&__restrict__ D,
				  cx_mat&__restrict__ T) {
	    int lwork = -1;
	    int m = in.rows();
	    int n = in.cols();
	    int lda = m;
	    int ldu = m;
	    int ldvt = n;
	    cx_double wkopt;
	    vec_t rwork(5 * m);
	    int info = -1;

	    cx_mat_t U_temp(m, m);
	    vec D_temp(m);
	    vec_t superb(std::min(m, n) - 1);
	    info = LAPACKE_zgesvd( LAPACK_COL_MAJOR, 'A', 'A', m, n, in.data(), m,
				   D_temp.data(), U_temp.data(), m, T.data(), n, superb.data());
	    U = U_temp.block(0, 0, m, n);
	    D = D_temp.segment(0, n);
	    // lwork = -1;
	    // zgesvd( "All", "All", &m, &n, in.data(), &lda, D.data(), U.data(), &ldu, T.data(),
	    // 	    &ldvt, &wkopt, &lwork,
	    // 	    rwork.data(), &info );
	    // lwork = int(wkopt.real());
	    // cx_vec_t work(lwork);
	    // /* Compute SVD */
	    // zgesvd( "All", "All", &m, &n, in.data(), &lda, D.data(), U.data(), &ldu, T.data(), &ldvt, work.data(), &lwork,
	    // 	    rwork.data(), &info );

	}

	inline void decompose_udt_full_piv(cx_mat&__restrict__ in,
					   cx_mat&__restrict__ U,
					   vec&__restrict__ D,
					   cx_mat&__restrict__ T) {
	    using namespace std;
	    using namespace Eigen;

	    if (U.rows() != in.rows() || U.cols() != in.cols()) {
		cout << U.rows() << " vs. " << in.rows() << "\tand\t"
		     << U.cols() << " vs. " << in.cols() << endl;
		throw std::runtime_error("dimensions of U are wrong");
	    }

	    if (D.size() != in.cols()) {
		cout << D.size() << endl;
		throw std::runtime_error("dimensions of D are wrong");
	    }

	    if (T.rows() != in.cols() || U.cols() != in.cols()) {
		cout << T.rows() << " vs. " << in.cols() << "\tand\t"
		     << U.cols() << " vs. " << in.cols() << endl;
		throw std::runtime_error("dimensions of T are wrong");
	    }

	    cx_mat_t scaled_in = in;
	    // perm_mat row_perm = dqmc::la::cx_row_sort(in, scaled_in);
	    // perm_mat col_perm = dqmc::la::cx_col_sort(scaled_in, in);

	    Eigen::FullPivHouseholderQR<cx_mat> qr(in);

	    qr.compute(in);
	    // U = row_perm.inverse() * qr.matrixQ().block(0, 0, in.rows(), in.cols());
	    U = qr.matrixQ().block(0, 0, in.rows(), in.cols());

	    cx_mat upper = qr.matrixQR().triangularView<Upper>();// .block(0, 0, in.cols(), in.cols());
	    // cout << "upper" << endl << upper << endl << endl;
	    D = upper.block(0, 0, in.cols(), in.cols()).diagonal().real().cast<double>();
	    vec d_inv = D;

	    for (int i = 0; i < in.cols(); ++i) {
		if (D(i) == 0.) {
		    // std::cout << "culprit " << D(i) << " was "
		    // 	      << upper(i, i) << std::endl;
		    dqmc::tools::abort("Invalid upper triangle"); }
		    // throw std::runtime_error("Invalid upper triangle"); }
		d_inv(i) = 1./D(i);
		if (D(i) < 0) { D(i) *= -1.;
		    d_inv(i) *= -1.; }
	    }
	    // cout << "D " << endl << D.transpose() << endl << endl;
	    // cout << "inv D " << endl << d_inv.transpose() << endl << endl;
	    T = (d_inv.asDiagonal() * upper.block(0, 0, in.cols(), in.cols()))
	    	* qr.colsPermutation().transpose(); // * col_perm.inverse();
	    if (D(0) != D(0)) std::cout << "NaN alert" << endl;
	}

	inline void decompose_udt_full_sort(cx_mat&__restrict__ in,
					    cx_mat&__restrict__ U,
					    vec&__restrict__ D,
					    cx_mat&__restrict__ T) {
	    using namespace std;
	    using namespace Eigen;

	    if (U.rows() != in.rows() || U.cols() != in.cols())
		throw std::runtime_error("dimensions of U are wrong");

	    if (D.size() != in.cols()) {
		cout << D.size() << endl;
		throw std::runtime_error("dimensions of D are wrong");
	    }

	    if (T.rows() != in.cols() || U.cols() != in.cols())
		throw std::runtime_error("dimensions of T are wrong");

	    cx_mat_t scaled_in;// = cx_mat_t::Zero(in.rows(), in.cols());
	    scaled_in.fill(cx_double(0., 0.));
	    perm_mat row_perm = dqmc::la::cx_row_sort(in, scaled_in);
	    perm_mat col_perm = dqmc::la::cx_col_sort(scaled_in, in);

	    // Very crude scaling...
	    //	    double scale = pow(in.cwiseAbs().maxCoeff(), 0.5);
	    double scale = 1.;

	    cx_vec_t taus(in.cols());
	    ivec iwork(in.cols());

	    // vec work(in.rows() * in.rows() * in.rows());
	    int lwork = -1;
	    // int info = 0;
	    int M = in.rows();
	    int N = in.cols();

	    scaled_in.block(0, 0, in.rows(), in.cols()) = in;

	    iwork.setZero();
	    lapack_int info = LAPACKE_zgeqp3( LAPACK_COL_MAJOR, M, N, scaled_in.data(),
					      M, iwork.data(), taus.data() );

	    // cout << iwork.size() << " " << work.size() << " " << taus.size() << endl;
	    // lwork = -1;
	    // zgeqp3_(&M, &N, scaled_in.data(), &M, iwork.data(),
	    // 	    taus.data(), taus.data(),
	    // 	    &lwork, &info);
	    // lwork = taus(0);
	    // cx_vec_t work(lwork);
	    // work.setZero();
	    // taus.setZero();

	    // zgeqp3_(&M, &N, scaled_in.data(), &M, iwork.data(),
	    // 	    taus.data(), work.data(),
	    // 	    &lwork, &info);

	    cx_mat_t upper = scaled_in.triangularView<Upper>();
	    D = upper.block(0, 0, in.cols(), in.cols()).diagonal().real().cast<double>();
	    vec d_inv = D;

	    for (int i = 0; i < in.cols(); ++i) {
		if (D(i) == 0.) {
		    // std::cout << "culprit " << D(i) << " was "
		    // 	      << upper(i, i) << std::endl;
		    dqmc::tools::abort("Invalid upper triangle"); }
		    // throw std::runtime_error("Invalid upper triangle"); }
		d_inv(i) = 1./D(i);
		if (D(i) < 0) { D(i) *= -1.;
		    d_inv(i) *= -1.; }
	    }
	    lwork = -1;

	    info = LAPACKE_zungqr( LAPACK_COL_MAJOR, M, N, N,
				   scaled_in.data(), M, taus.data() );

	    // zungqr_(&M, &N, &N, scaled_in.data(), &M, taus.data(),
	    // 	    work.data(), &lwork, &info);
	    // // if (work(0) > work.size()) {
	    // // 	cout << "Need more lwork for dorgqr" << endl;
	    // // }
	    // lwork = work(0);
	    // work.resize(lwork);
	    // // cout << "in" << endl << scaled_in << endl << endl;
	    // zungqr_(&M, &N, &N, scaled_in.data(), &M, taus.data(),
	    // 	    work.data(), &lwork, &info);
	    // cout << "out" << endl << scaled_in << endl << endl;

	    U = row_perm.inverse() * scaled_in.block(0, 0, in.rows(), in.cols());
	    ivec col_ids(in.cols());
	    for (int i = 0; i < in.cols(); ++i) {
		col_ids(i) = iwork(i) - 1;
	    }

	    Eigen::PermutationMatrix<Eigen::Dynamic, Eigen::Dynamic> lapack_col_perm(col_ids);
	    T = (d_inv.cast<cx_double_t>().asDiagonal()
		 * upper.block(0, 0, in.cols(), in.cols()))
		* lapack_col_perm.transpose() * col_perm.transpose();
	    D *= scale;
	    if (D(0) != D(0)) std::cout << "NaN alert" << endl;
	}

	inline void decompose_udt_col_piv(cx_mat&__restrict__ in,
					    cx_mat&__restrict__ U,
					    vec&__restrict__ D,
					    cx_mat&__restrict__ T) {
	    using namespace std;
	    using namespace Eigen;
	    if (U.rows() != in.rows() || U.cols() != in.cols() || U.cols() == 0) {
		cout << U.rows() << " " << U.cols() << endl;
		throw std::runtime_error("dimensions of U are wrong");
	    }

	    if (D.size() != in.cols() || D.size() == 0) {
		cout << D.size() << endl;
		throw std::runtime_error("dimensions of D are wrong");
	    }

	    if (T.rows() != in.cols() || T.cols() == 0)
		throw std::runtime_error("dimensions of T are wrong");

	    cx_mat_t scaled_in = cx_mat_t::Zero(in.rows(), in.cols());
	    scaled_in.fill(cx_double(0., 0.));

	    cx_vec_t taus(in.cols());
	    ivec iwork(in.cols());

	    int lwork = -1;
	    int M = in.rows();
	    int N = in.cols();

	    perm_mat row_sort_perm = cx_row_sort(in, scaled_in);
	    // scaled_in.block(0, 0, in.rows(), in.cols()) = in;
	    iwork.setZero();
	    lapack_int info = LAPACKE_zgeqp3( LAPACK_COL_MAJOR, M, N, scaled_in.data(),
					      M, iwork.data(), taus.data() );

	    cx_mat_t upper = scaled_in.triangularView<Upper>();
	    D = upper.block(0, 0, in.cols(), in.cols()).diagonal().real().cast<double>();
	    vec d_inv = D;

	    for (int i = 0; i < in.cols(); ++i) {
		if (D(i) == 0.) {
		    // std::cout << "culprit " << D(i) << " was "
		    // 	      << upper(i, i) << std::endl;
		    dqmc::tools::abort("Invalid upper triangle");
		    // throw std::runtime_error("Invalid upper triangle");
		}
		d_inv(i) = 1./D(i);
		if (D(i) < 0) { D(i) *= -1.;
		    d_inv(i) *= -1.; }
	    }
	    lwork = -1;

	    info = LAPACKE_zungqr( LAPACK_COL_MAJOR, M, N, N,
				   scaled_in.data(), M, taus.data() );

	    U = (row_sort_perm.inverse() * scaled_in).block(0, 0, in.rows(), in.cols());
	    ivec col_ids(in.cols());
	    for (int i = 0; i < in.cols(); ++i) {
		col_ids(i) = iwork(i) - 1;
	    }

	    Eigen::PermutationMatrix<Eigen::Dynamic, Eigen::Dynamic> lapack_col_perm(col_ids);
	    T = (d_inv.cast<cx_double_t>().asDiagonal()
		 * upper.block(0, 0, in.cols(), in.cols()))
		* lapack_col_perm.transpose();
	    if (D(0) != D(0)) std::cout << "NaN alert" << endl;
	}

	inline void decompose_udt(cx_mat&__restrict__ in,
				  cx_mat&__restrict__ U,
				  vec&__restrict__ D,
				  cx_mat&__restrict__ T) {
	    using namespace std;
	    using namespace Eigen;
	    if (U.rows() != in.rows() || U.cols() != in.cols() || U.cols() == 0) {
		cout << U.rows() << " " << U.cols() << endl;
		throw std::runtime_error("dimensions of U are wrong");
	    }

	    if (D.size() != in.cols() || D.size() == 0) {
		cout << D.size() << endl;
		throw std::runtime_error("dimensions of D are wrong");
	    }

	    if (T.rows() != in.cols() || T.cols() == 0)
		throw std::runtime_error("dimensions of T are wrong");

	    cx_mat_t scaled_in = cx_mat_t::Zero(in.rows(), in.cols());
	    scaled_in.fill(cx_double(0., 0.));

	    cx_vec_t taus(in.cols());
	    ivec iwork(in.cols());

	    int lwork = -1;
	    int M = in.rows();
	    int N = in.cols();

	    scaled_in.block(0, 0, in.rows(), in.cols()) = in;
	    iwork.setZero();

	    lapack_int info = LAPACKE_zgeqrf( LAPACK_COL_MAJOR, M, N,
					      scaled_in.data(),
					      M, taus.data() );

	    cx_mat_t upper = scaled_in.triangularView<Upper>();
	    D = upper.block(0, 0, in.cols(), in.cols()).diagonal().real().cast<double>();
	    vec d_inv = D;

	    for (int i = 0; i < in.cols(); ++i) {
		if (D(i) == 0.) {
		    dqmc::tools::abort("Invalid upper triangle");
		}
		    // throw std::runtime_error("Invalid upper triangle"); }
		d_inv(i) = 1./D(i);
		if (D(i) < 0) { D(i) *= -1.;
		    d_inv(i) *= -1.; }
	    }
	    lwork = -1;

	    info = LAPACKE_zungqr( LAPACK_COL_MAJOR, M, N, N,
				   scaled_in.data(), M, taus.data() );

	    U = scaled_in.block(0, 0, in.rows(), in.cols());
	    ivec col_ids(in.cols());
	    for (int i = 0; i < in.cols(); ++i) {
		col_ids(i) = iwork(i) - 1;
	    }

	    T = (d_inv.cast<cx_double_t>().asDiagonal()
		 * upper.block(0, 0, in.cols(), in.cols()));
	    if (D(0) != D(0)) std::cout << "NaN alert" << endl;
	}
    }
}
#endif


// Boost.Geometry
// Unit Test

// Copyright (c) 2019 Oracle and/or its affiliates.

// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

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

#include <sstream>

#include "../formulas/test_formula.hpp"
#include "distance_cross_track_cases.hpp"

#include <boost/geometry/strategies/strategies.hpp>
#include <boost/geometry/srs/spheroid.hpp>

struct error{
    double long_distance;
    double short_distance;
    double very_short_distance;
    double very_very_short_distance;
};

void check_result(double const& result, double const& expected,
                  double const& reference, error const& reference_error)
{
    BOOST_GEOMETRY_CHECK_CLOSE(result, expected, 0.1,
    std::setprecision(20) << "result {" << result
                          << "} different than expected {" << expected << "}.");

    double reference_error_value = result > 2000 ? reference_error.long_distance
                                 : result > 100  ? reference_error.short_distance
                                 : result > 20   ? reference_error.very_short_distance
                                 : reference_error.very_very_short_distance;

    BOOST_GEOMETRY_CHECK_CLOSE(result, reference, reference_error_value,
        std::setprecision(20) << "result {" << result
                              << "} different than reference {"
                              << reference << "}.");
}

template <typename Point>
void test_all(expected_results const& results)
{
    double const d2r = bg::math::d2r<double>();

    double lon1r = results.p1.lon * d2r;
    double lat1r = results.p1.lat * d2r;
    double lon2r = results.p2.lon * d2r;
    double lat2r = results.p2.lat * d2r;
    double lon3r = results.p3.lon * d2r;
    double lat3r = results.p3.lat * d2r;

    typedef bg::srs::spheroid<double> Spheroid;

    // WGS84
    Spheroid spheroid(6378137.0, 6356752.3142451793);

    error errors [] =
    {
        {0.00000001, 0.00000001, 0.00000001, 0.000001}, //vincenty
        {0.0002, 0.002, 0.01, 0.2}, //thomas
        {0.002, 0.4, 15, 25}, //andoyer
        {1, 6, 15, 200} //spherical
    };

    //vincenty
    double distance = bg::strategy::distance::detail::geographic_cross_track<bg::strategy::vincenty, Spheroid, double, true>(spheroid)
            .apply(Point(lon3r, lat3r), Point(lon1r, lat1r), Point(lon2r, lat2r));
    check_result(distance, results.vincenty_bisection, results.reference, errors[0]);

    distance = bg::strategy::distance::geographic_cross_track<bg::strategy::vincenty, Spheroid, double>(spheroid)
            .apply(Point(lon3r, lat3r), Point(lon1r, lat1r), Point(lon2r, lat2r));
    check_result(distance, results.vincenty, results.reference, errors[0]);

    //thomas
    distance = bg::strategy::distance::detail::geographic_cross_track<bg::strategy::thomas, Spheroid, double, true>(spheroid)
            .apply(Point(lon3r, lat3r), Point(lon1r, lat1r), Point(lon2r, lat2r));
    check_result(distance, results.thomas_bisection, results.reference, errors[1]);

    distance = bg::strategy::distance::geographic_cross_track<bg::strategy::thomas, Spheroid, double>(spheroid)
            .apply(Point(lon3r, lat3r), Point(lon1r, lat1r), Point(lon2r, lat2r));
    check_result(distance, results.thomas, results.reference, errors[1]);

    //andoyer
    distance = bg::strategy::distance::detail::geographic_cross_track<bg::strategy::andoyer, Spheroid, double, true>(spheroid)
            .apply(Point(lon3r, lat3r), Point(lon1r, lat1r), Point(lon2r, lat2r));
    check_result(distance, results.andoyer_bisection, results.reference, errors[2]);

    distance = bg::strategy::distance::geographic_cross_track<bg::strategy::andoyer, Spheroid, double>(spheroid)
            .apply(Point(lon3r, lat3r), Point(lon1r, lat1r), Point(lon2r, lat2r));
    check_result(distance, results.andoyer, results.reference, errors[2]);

    //spherical
    distance = bg::strategy::distance::cross_track<>(bg::formula::mean_radius<double>(spheroid))
            .apply(Point(lon3r, lat3r), Point(lon1r, lat1r), Point(lon2r, lat2r));
    check_result(distance, results.spherical, results.reference, errors[3]);

}

int test_main(int, char*[])
{
    typedef bg::model::point<double, 2, bg::cs::geographic<bg::radian> > point;

    for (size_t i = 0; i < expected_size; ++i)
    {
        test_all<point>(expected[i]);
    }

    return 0;
}


// -*- mode: c++; indent-tabs-mode: nil; -*-
//
// Copyright (c) 2009-2013 Illumina, Inc.
//
// This software is provided under the terms and conditions of the
// Illumina Open Source Software License 1.
//
// You should have received a copy of the Illumina Open Source
// Software License 1 along with this program. If not, see
// <https://github.com/sequencing/licenses/>
//

/// \file

/// \author Chris Saunders
///
#include "blt_util/stat_util.hh"

#include <boost/math/distributions/chi_squared.hpp>

bool
is_chi_sqr_reject(const double xsq,
                  const unsigned df,
                  const double alpha) {

    assert(xsq>=0);
    assert(df>0);

    boost::math::chi_squared dist(df);
    return ((1.-boost::math::cdf(dist,xsq)) < alpha);

#if 0
    // alternate implementation (is one faster?):
    const double xsq_crit_val(boost::math::quantile(dist,1.-alpha));
    return xsq>xsq_crit_val;
#endif
}

bool
is_lrt_reject_null(const double null_loghood,
                   const double alt_loghood,
                   const unsigned df,
                   const double alpha) {

    if (df == 0) return false;
    if (null_loghood>alt_loghood) return false;

    const double log_lrt(-2.*(null_loghood-alt_loghood));

    return is_chi_sqr_reject(log_lrt,df,alpha);
}


#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);
  Vector3d v = Vector3d::Random(), w;
Projective3d P(Matrix4d::Random());
cout << "v                                   = [" << v.transpose() << "]^T" << endl;
cout << "h.homogeneous()                     = [" << v.homogeneous().transpose() << "]^T" << endl;
cout << "(P * v.homogeneous())               = [" << (P * v.homogeneous()).transpose() << "]^T" << endl;
cout << "(P * v.homogeneous()).hnormalized() = [" << (P * v.homogeneous()).eval().hnormalized().transpose() << "]^T" << endl;
  return 0;
}


#include <vector>
#include <string>
#include <iostream>
#include <boost/tuple/tuple.hpp>
#include <set>

int findBestPack( const std::vector<boost::tuple<std::string , int , int> > & ,
      std::set<int> & , const int  ) ;

int main( ) {
   std::vector<boost::tuple<std::string , int , int> > items ;
   //===========fill the vector with data====================
   items.push_back( boost::make_tuple( "" , 0  ,  0 ) ) ;
   items.push_back( boost::make_tuple( "map" , 9 , 150 ) ) ;
   items.push_back( boost::make_tuple( "compass" , 13 , 35 ) ) ;
   items.push_back( boost::make_tuple( "water" , 153 , 200 ) ) ;
   items.push_back( boost::make_tuple( "sandwich", 50 , 160 ) ) ;
   items.push_back( boost::make_tuple( "glucose" , 15 , 60 ) ) ;
   items.push_back( boost::make_tuple( "tin", 68 , 45 ) ) ;
   items.push_back( boost::make_tuple( "banana", 27 , 60 ) ) ;
   items.push_back( boost::make_tuple( "apple" , 39 , 40 ) ) ;
   items.push_back( boost::make_tuple( "cheese" , 23 , 30 ) ) ;
   items.push_back( boost::make_tuple( "beer" , 52 , 10 ) ) ;
   items.push_back( boost::make_tuple( "suntan creme" , 11 , 70 ) ) ;
   items.push_back( boost::make_tuple( "camera" , 32 , 30 ) ) ;
   items.push_back( boost::make_tuple( "T-shirt" , 24 , 15 ) ) ;
   items.push_back( boost::make_tuple( "trousers" , 48 , 10 ) ) ;
   items.push_back( boost::make_tuple( "umbrella" , 73 , 40 ) ) ;
   items.push_back( boost::make_tuple( "waterproof trousers" , 42 , 70 ) ) ;
   items.push_back( boost::make_tuple( "waterproof overclothes" , 43 , 75 ) ) ;
   items.push_back( boost::make_tuple( "note-case" , 22 , 80 ) ) ;
   items.push_back( boost::make_tuple( "sunglasses" , 7 , 20 ) ) ;
   items.push_back( boost::make_tuple( "towel" , 18 , 12 ) ) ;
   items.push_back( boost::make_tuple( "socks" , 4 , 50 ) ) ;
   items.push_back( boost::make_tuple( "book" , 30 , 10 ) ) ;
   const int maximumWeight = 400 ;
   std::set<int> bestItems ; //these items will make up the optimal value
   int bestValue = findBestPack( items , bestItems , maximumWeight ) ;
   std::cout << "The best value that can be packed in the given knapsack is " <<
      bestValue << " !\n" ;
   int totalweight = 0 ;
   std::cout << "The following items should be packed in the knapsack:\n" ;
   for ( std::set<int>::const_iterator si = bestItems.begin( ) ;
	 si != bestItems.end( ) ; si++ ) {
      std::cout << (items.begin( ) + *si)->get<0>( ) << "\n" ;
      totalweight += (items.begin( ) + *si)->get<1>( ) ;
   }
   std::cout << "The total weight of all items is " << totalweight << " !\n" ;
   return 0 ;
}

int findBestPack( const std::vector<boost::tuple<std::string , int , int> > & items ,std::set<int> & bestItems , const int weightlimit ) {
   //dynamic programming approach sacrificing storage space for execution
   //time , creating a table of optimal values for every weight and a
   //second table of sets with the items collected so far in the knapsack
   //the best value is in the bottom right corner of the values table,
   //the set of items in the bottom right corner of the sets' table.
   const int n = items.size( ) ;
   int bestValues [ n ][ weightlimit ] ;
   std::set<int> solutionSets[ n ][ weightlimit ] ;
   std::set<int> emptyset ;
   for ( int i = 0 ; i < n ; i++ ) {
      for ( int j = 0 ; j < weightlimit  ; j++ ) {
	 bestValues[ i ][ j ] = 0 ;
	 solutionSets[ i ][ j ] = emptyset ;
       }
    }
    for ( int i = 0 ; i < n ; i++ ) {
       for ( int weight = 0 ; weight < weightlimit ; weight++ ) {
	  if ( i == 0 )
	     bestValues[ i ][ weight ] = 0 ;
	  else  {
	     int itemweight = (items.begin( ) + i)->get<1>( ) ;
	     if ( weight < itemweight ) {
		bestValues[ i ][ weight ] = bestValues[ i - 1 ][ weight ] ;
		solutionSets[ i ][ weight ] = solutionSets[ i - 1 ][ weight ] ;
	     } else { // weight >= itemweight
		if ( bestValues[ i - 1 ][ weight - itemweight ] +
		   (items.begin( ) + i)->get<2>( ) >
		        bestValues[ i - 1 ][ weight ] ) {
		   bestValues[ i ][ weight ] =
		       bestValues[ i - 1 ][ weight - itemweight ] +
	        	(items.begin( ) + i)->get<2>( ) ;
		  solutionSets[ i ][ weight ] =
		      solutionSets[ i - 1 ][ weight - itemweight ] ;
		  solutionSets[ i ][ weight ].insert( i ) ;
	     }
	     else {
		bestValues[ i ][ weight ] = bestValues[ i - 1 ][ weight ] ;
		solutionSets[ i ][ weight ] = solutionSets[ i - 1 ][ weight ] ;
	     }
	  }
       }
      }
    }
    bestItems.swap( solutionSets[ n - 1][ weightlimit - 1 ] ) ;
    return bestValues[ n - 1 ][ weightlimit - 1 ] ;
}


#include <sparse.h>

#define BOOST_AUTO_TEST_MAIN
#include <boost/test/auto_unit_test.hpp>
#include <boost/test/unit_test_suite.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <boost/test/test_tools.hpp>

BOOST_AUTO_TEST_SUITE(SPARSE);

BOOST_AUTO_TEST_CASE(inverse_test)
{
  typedef sparse::Block<1,1,float>   block_type;
  typedef sparse::DiagonalMatrix<block_type> matrix_type;

  matrix_type D;

  D.resize(4);

  D(0) =  2.0;
  D(1) =  4.0;
  D(2) =  8.0;
  D(3) = 16.0;

  sparse::inverse( D );

  BOOST_CHECK( D(0) == 1.0f/2.0f );
  BOOST_CHECK( D(1) == 1.0f/4.0f );
  BOOST_CHECK( D(2) == 1.0f/8.0f );
  BOOST_CHECK( D(3) == 1.0f/16.0f );

}

BOOST_AUTO_TEST_SUITE_END();


#pragma once

#include <armadillo>
#include <iostream>
#include "shannonEntropy.hpp"
#include <Eigen/Dense>

// using Eigen::ArrayXi;

using ArrayXL = Eigen::Array<int64_t, Eigen::Dynamic, 1>;

using namespace std;
// using namespace arma;

// static_assert(sizeof(sword) ==8); //findHFPair works on the assumption that arma::sword is 64 bit

struct ETC {

    union pair {
        struct {
            uint64_t i1;
            uint64_t i2;
        } __attribute__((packed));
        __int128 i128;

        bool operator==(const pair& other)
        {
            return i128 == other.i128;
        }
    };

   static ETC::pair makeETCPair (uint64_t a, uint64_t b) {ETC::pair p; p.i1 = a; p.i2 = b; return p;};

   typedef unordered_map<__int128, unsigned int> pairFreqTable;

    static ETC::pair findHFPair(const ArrayXL &seq) {
        /*
         this implementation works very slightly differently from the matlab original where more that one pair wins the highest frequency
         - the matlab version depends on arbitrary behaviour of max(x)
         [m,indx]=max(Count_Array(:));
         i.e. chooses winner based on first position in 2d frequency matrix
         whereas this version chooses the first winner in the order of the array, which is slightly faster
         tests show this occasionally makes very minor differences in the results
         */
        ETC::pairFreqTable histo;
        ETC::pair winner;
        unsigned int highScore=0;

        unsigned int seqPos = 0;
        while (seqPos < seq.size()-1) {
            ETC::pair currPair = ETC::makeETCPair(seq[seqPos], seq[seqPos+1]);
            ETC::pairFreqTable::iterator it = histo.find(currPair.i128);
            unsigned int score;
            if (it == histo.end()) {
                histo.insert(std::make_pair(currPair.i128, 1));
                score=1;
            }else{
                score = it->second + 1;
               it->second = score;
            }
            if (score > highScore) {
                highScore = score;
                winner = currPair;
            }
            if (currPair.i1 == currPair.i2) {
                if (seqPos < seq.size()-2) {
                    if (seq[seqPos+2] == seq[seqPos]) {
                        seqPos++;
                    }
                }
            }
            seqPos++;
        }
        return winner;
    }

    static auto substitute(const ArrayXL &seq, ETC::pair p) {
        int64_t replacementSymbol = seq.maxCoeff() + 1;
        ArrayXL newSeq(seq.size());
        size_t src=0, dest=0;
        size_t replaceCount=0;
        while(src < seq.size()) {
            if (src < seq.size()-1) {
                if (seq[src] == p.i1 && seq[src+1] == p.i2) {
                    newSeq[dest] = replacementSymbol;
                    src++;
                    replaceCount++;
                } else {
                    newSeq[dest] = seq[src];
                }
            }else{
                newSeq[dest] = seq[src];
            }
            src++;
            dest++;
        }
        ArrayXL finalSeq = newSeq.head(dest);
        return std::make_tuple(finalSeq, replaceCount, replacementSymbol);
    }

    // static double calcOld(const ivec &seq) {
    //     double N = 0; //ETC measure
    //     double Hnew = shannonEntropy::calc(seq);
    //     ivec newSeq = seq;

    //     //todo: this can be optimised by continually editing the shannon histo instead of redoing it every time
    //     while(Hnew >1e-6 && newSeq.size() > 1) {
    //         ETC::pair hfPair = ETC::findHFPair(newSeq);
    //         auto [newSeqRepl, replaceCount, replaceSym] = ETC::substitute(newSeq, hfPair);
    //         Hnew = shannonEntropy::calc(newSeqRepl);
    //         newSeq = newSeqRepl;
    //         N++;
    //         // cout << newSeq << endl;
    //         // cout << N << ", " << Hnew << endl;
    //     }

    //     return N;
    // }

    static double calc(const ArrayXL &seq) {
        double N = 0; //ETC measure
//        cout << seq << endl;
        if (seq.size() > 1) {
            shannonEntropy::histoMap histo = shannonEntropy::calcDistribution(seq);
            double Hnew = shannonEntropy::calcProbability(histo, seq);

            ArrayXL newSeq = seq;

            while(Hnew >1e-6 && newSeq.size() > 1) {
                ETC::pair hfPair = ETC::findHFPair(newSeq);
                auto [newSeqRepl, replaceCount, replaceSym] = ETC::substitute(newSeq, hfPair);
                //reduce counts of replacement pair
                shannonEntropy::histoMap::iterator it = histo.find(hfPair.i1);
                it->second -= replaceCount;
                if (it->second == 0) {
                    //remove from the histo
                    histo.erase(it);
                }
                it = histo.find(hfPair.i2);
                it->second -= replaceCount;
                if (it->second == 0) {
                    //remove from the histo
                    histo.erase(it);
                }
                //add the new symbol into the histogram
                auto histoEntry = make_pair(replaceSym, replaceCount);
                histo.insert(histoEntry);

                Hnew = shannonEntropy::calcProbability(histo, newSeqRepl);
                newSeq = newSeqRepl;
                N++;
            }
            N /= (seq.size() -1);
        }
        return N;
    }

    static double calcJoint(const ArrayXL& seq1, const ArrayXL& seq2) {

        ArrayXL combSeq(seq1.size());
        for (size_t i=0; i < seq1.size(); i++) {
            combSeq[i] =(uint64_t)( seq1[i] | (seq2[i] << 32));
        }
        return ETC::calc(combSeq);
    }

};


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

  @copyright 2016 NumScale SAS

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

**/
//==================================================================================================
#ifndef BOOST_SIMD_HYPERBOLIC_HPP_INCLUDED
#define BOOST_SIMD_HYPERBOLIC_HPP_INCLUDED

namespace boost { namespace simd
{
  /*!
    @ingroup group-functions
    @defgroup group-hyperbolic  Hyperbolic functions

    These functions provides scalar and SIMD version of
    hyperbolic  and inverse hyperbolic functions.

    - Direct hyperbolic functors

         | name          | name         | name           | name         |
         |:-------------:|:------------:|:--------------:|:------------:|
         | @ref cosh     | @ref csch    | @ref sinh      | @ref sinhcosh|
         | @ref coth     | @ref sech    | @ref sinhc     | @ref tanh    |

          @ref sinhc is the hyperbolic sinus cardinal function
          (\f$\sinh x/x\f$).

          @ref sinhcosh  allows the simultaneous
          computation of hyperbolic sine and cosine at lower cost.

          @ref sech and @ref csch are the hyperbolic secant (inverse of
          the hyperbolic cosine) and cosecant (inverse of the hyperbolic sine)

    - Inverse hyperbolic functors

         | name        | name         | name        |
         |:-----------:|:------------:|:-----------:|
         | @ref acosh  | @ref acsch   | @ref asinh  |
         | @ref acoth  | @ref asech   | @ref atanh  |

    **/

} }

#include <boost/simd/function/acosh.hpp>
#include <boost/simd/function/acoth.hpp>
#include <boost/simd/function/acsch.hpp>
#include <boost/simd/function/asech.hpp>
#include <boost/simd/function/asinh.hpp>
#include <boost/simd/function/atanh.hpp>
#include <boost/simd/function/cosh.hpp>
#include <boost/simd/function/coth.hpp>
#include <boost/simd/function/csch.hpp>
#include <boost/simd/function/sech.hpp>
#include <boost/simd/function/sinhc.hpp>
#include <boost/simd/function/sinhcosh.hpp>
#include <boost/simd/function/sinh.hpp>
#include <boost/simd/function/tanh.hpp>

#endif


#include <boost/config.hpp>
#include <boost/version.hpp>
#include <fstream>

// CGAL headers
#include <CGAL/Cartesian.h>
#include <CGAL/MP_Float.h>
#include <CGAL/Algebraic_kernel_for_circles_2_2.h>
#include <CGAL/Circular_kernel_2.h>
#include <CGAL/intersections.h>
#include <CGAL/Circular_kernel_2.h>
#include <CGAL/Object.h>
#include <CGAL/IO/WKT.h>

// Qt headers
#include <QtGui>
#include <QString>
#include <QActionGroup>
#include <QFileDialog>
#include <QInputDialog>

// GraphicsView items and event filters (input classes)
#include <CGAL/Qt/GraphicsViewCircularArcInput.h>
#include "ArcsGraphicsItem.h"

// the two base classes
#include "ui_Circular_kernel_2.h"
#include <CGAL/Qt/DemosMainWindow.h>

typedef CGAL::Quotient<CGAL::MP_Float>                       NT;
typedef CGAL::Cartesian<NT>                                 Linear_k;

typedef CGAL::Algebraic_kernel_for_circles_2_2<NT>          Algebraic_k;
typedef CGAL::Circular_kernel_2<Linear_k,Algebraic_k>       CircularKernel;

typedef CircularKernel::Point_2                                 Point_2;
typedef CircularKernel::Segment_2                               Segment_2;
typedef CircularKernel::Line_arc_2                              Line_arc_2;
typedef CircularKernel::Circular_arc_2                          Circular_arc_2;
typedef CircularKernel::Circular_arc_point_2                    Circular_arc_point_2;

typedef CGAL::Qt::ArcsGraphicsItem<CircularKernel>                 ArcsGraphicsItem;

typedef std::vector<CGAL::Object>                           ArcContainer;

class MainWindow :
  public CGAL::Qt::DemosMainWindow,
  public Ui::Circular_kernel_2
{
  Q_OBJECT

private:
  ArcContainer arcs;
  ArcContainer intersections;
  QGraphicsScene scene;

  ArcsGraphicsItem * agi;

  CGAL::Qt::GraphicsViewCircularArcInput<CircularKernel> * cai;

public:
  MainWindow();

public Q_SLOTS:

  virtual void open(QString);

  void processInput(CGAL::Object o);

  void on_actionInsertCircularArc_toggled(bool checked);

  void on_actionClear_triggered();

  void on_actionLoadLineAndCircularArcs_triggered();

  void on_actionRecenter_triggered();

Q_SIGNALS:
  void changed();
};

MainWindow::MainWindow()
  : DemosMainWindow()
{
  setupUi(this);

  // Add a GraphicItem for the Circular triangulation
  agi = new CGAL::Qt::ArcsGraphicsItem<CircularKernel>(arcs, intersections);

  agi->setIntersectionsPen(QPen(Qt::red, 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));

  QObject::connect(this, SIGNAL(changed()),
                   agi, SLOT(modelChanged()));

  agi->setInputPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
  scene.addItem(agi);
  agi->hide();

  // Setup input handlers. They get events before the scene gets them
  // and the input they generate is passed to the triangulation with
  // the signal/slot mechanism
  cai = new CGAL::Qt::GraphicsViewCircularArcInput<CircularKernel>(this, &scene);

  QObject::connect(cai, SIGNAL(generate(CGAL::Object)),
                   this, SLOT(processInput(CGAL::Object)));

  // Manual handling of actions
  //
  QObject::connect(this->actionQuit, SIGNAL(triggered()),
                   qApp, SLOT(quit()));

  // Check two actions
  this->actionInsertCircularArc->setChecked(true);

  //
  // Setup the scene and the view
  //
  scene.setItemIndexMethod(QGraphicsScene::NoIndex);
  scene.setSceneRect(-100, -100, 100, 100);
  this->graphicsView->setScene(&scene);

  // Uncomment the following line to get antialiasing by default.
//   actionUse_Antialiasing->setChecked(true);

  // Turn the vertical axis upside down
  this->graphicsView->scale(1, -1);

  // The navigation adds zooming and translation functionality to the
  // QGraphicsView
  this->addNavigation(this->graphicsView);

  this->setupStatusBar();
  this->setupOptionsMenu();
  this->addAboutDemo(":/cgal/help/about_Circular_kernel_2.html");
  this->addAboutCGAL();
  this->addRecentFiles(this->menuFile, this->actionQuit);
  connect(this, SIGNAL(openRecentFile(QString)),
          this, SLOT(open(QString)));
}

void
MainWindow::processInput(CGAL::Object o)
{
  Circular_arc_2 ca;
  Line_arc_2 la;
  bool is_circular = false;

  if(assign(ca, o)){
    is_circular = true;
  } else if(! assign(la, o)){
    std::cerr << "unknown object" << std::endl;
    return;
  }

  for(std::vector<CGAL::Object>::iterator it = arcs.begin(); it != arcs.end(); ++it){
    Circular_arc_2 vca;
    Line_arc_2 vla;
    if(assign(vca, *it)){
      if(is_circular){
        CGAL::intersection(ca, vca, std::back_inserter(intersections));
      } else {
        CGAL::intersection(la, vca, std::back_inserter(intersections));
      }
    } else if(assign(vla, *it)){
      if(is_circular){
        CGAL::intersection(ca, vla, std::back_inserter(intersections));
      } else {
        CGAL::intersection(la, vla, std::back_inserter(intersections));
      }
    }
  }
  arcs.push_back(o);
  Q_EMIT( changed());
}

/*
 *  Qt Automatic Connections
 *  https://doc.qt.io/qt-5/designer-using-a-ui-file.html#automatic-connections
 *
 *  setupUi(this) generates connections to the slots named
 *  "on_<action_name>_<signal_name>"
 */

void
MainWindow::on_actionInsertCircularArc_toggled(bool checked)
{
  if(checked){
    scene.installEventFilter(cai);
  } else {
    scene.removeEventFilter(cai);
  }
}

void
MainWindow::on_actionClear_triggered()
{
  arcs.clear();
  intersections.clear();
  Q_EMIT( changed());
}

void
MainWindow::on_actionLoadLineAndCircularArcs_triggered()
{
  QString fileName = QFileDialog::getOpenFileName(this,
                                                  tr("Open Line and Circular Arc File"),
                                                  ".",
                                                  tr("Edge files (*.arc)\n"
                                                     "WKT files (*.wkt *.WKT)\n"
                                                     ));
  if(! fileName.isEmpty()){
    open(fileName);
    this->addToRecentFiles(fileName);
  }
}

void
MainWindow::open(QString fileName)
{
    std::ifstream ifs(qPrintable(fileName));

    char c;
    double x,y;
    if(fileName.endsWith(".wkt", Qt::CaseInsensitive))
    {
      //read pairs as Line_arc_2 and triplets as circular_arc_2
      do
      {
        std::vector<Point_2> multi_points;
        CGAL::IO::read_multi_point_WKT(ifs, multi_points);
        if(multi_points.size() == 2)
        {
          Line_arc_2 la(Segment_2(multi_points[0],
                        multi_points[1]));
          for(std::vector<CGAL::Object>::iterator it = arcs.begin(); it != arcs.end(); ++it){
            Circular_arc_2 vca;
            Line_arc_2 vla;
            if(assign(vca, *it)){
              CGAL::intersection(la, vca, std::back_inserter(intersections));
            } else if(assign(vla, *it)){
              CGAL::intersection(la, vla, std::back_inserter(intersections));
            }
          }
          arcs.push_back(make_object(la));
        }
        else if(multi_points.size() == 3)
        {
          Circular_arc_2 ca(multi_points[0],
                            multi_points[1],
                            multi_points[2]);
          for(std::vector<CGAL::Object>::iterator it = arcs.begin(); it != arcs.end(); ++it){
            Circular_arc_2 vca;
            Line_arc_2 vla;
            if(assign(vca, *it)){
              CGAL::intersection(ca, vca, std::back_inserter(intersections));
            } else if(assign(vla, *it)){
              CGAL::intersection(ca, vla, std::back_inserter(intersections));
            }
          }
          arcs.push_back(make_object(ca));
        }
        else if(multi_points.size()>0)
        {
          std::cerr<<"unreadable object."<<std::endl;
        }
      }while(ifs.good() && !ifs.eof());
      ifs.close();
    }
    else
    {
      while(ifs >> c){
        if(c == 's'){
          ifs >> x >> y;
          Point_2 p(x,y);
          ifs >> x >> y;
          Point_2 q(x,y);

          Line_arc_2 la(Segment_2(p,q));
          for(std::vector<CGAL::Object>::iterator it = arcs.begin(); it != arcs.end(); ++it){
            Circular_arc_2 vca;
            Line_arc_2 vla;
            if(assign(vca, *it)){
              CGAL::intersection(la, vca, std::back_inserter(intersections));
            } else if(assign(vla, *it)){
              CGAL::intersection(la, vla, std::back_inserter(intersections));
            }
          }
          arcs.push_back(make_object(la));
        } else if(c == 'c'){
          ifs >> x >> y;
          Point_2 p(x,y);
          ifs >> x >> y;
          Point_2 q(x,y);
          ifs >> x >> y;
          Point_2 r(x,y);
          Circular_arc_2 ca(p,q,r);
          for(std::vector<CGAL::Object>::iterator it = arcs.begin(); it != arcs.end(); ++it){
            Circular_arc_2 vca;
            Line_arc_2 vla;
            if(assign(vca, *it)){
              CGAL::intersection(ca, vca, std::back_inserter(intersections));
            } else if(assign(vla, *it)){
              CGAL::intersection(ca, vla, std::back_inserter(intersections));
            }
          }
          arcs.push_back(make_object(ca));
        }
      }
    }
    Q_EMIT( changed());
}

void
MainWindow::on_actionRecenter_triggered()
{
  this->graphicsView->setSceneRect(agi->boundingRect());
  this->graphicsView->fitInView(agi->boundingRect(), Qt::KeepAspectRatio);
}

#include "Circular_kernel_2.moc"
#include <CGAL/Qt/resources.h>

int main(int argc, char **argv)
{
  QApplication app(argc, argv);

  app.setOrganizationDomain("geometryfactory.com");
  app.setOrganizationName("GeometryFactory");
  app.setApplicationName("Circular_kernel_2 demo");

  // Import resources from libCGAL (Qt5).
  CGAL_QT_INIT_RESOURCES;

  MainWindow mainWindow;
  mainWindow.show();
  return app.exec();
}


// Distributed under the MIT License.
// See LICENSE.txt for details.

// Defines the class BulgedCube.

#pragma once

#include <array>
#include <boost/optional.hpp>
#include <cstddef>
#include <limits>

#include "DataStructures/Tensor/TypeAliases.hpp"
#include "Utilities/TypeTraits/RemoveReferenceWrapper.hpp"

/// \cond
namespace PUP {
class er;
}  // namespace PUP
/// \endcond

namespace domain {
namespace CoordinateMaps {

/*!
 *  \ingroup CoordinateMapsGroup
 *
 *  \brief Three dimensional map from the cube to a bulged cube.
 *  The cube is shaped such that the surface is compatible
 *  with the inner surface of Wedge3D.
 *  The shape of the object can be chosen to be cubical,
 *  if the sphericity is set to 0, or to a sphere, if
 *  the sphericity is set to 1. The sphericity can
 *  be set to any number between 0 and 1 for a bulged cube.
 *
 *  \details The volume map from the cube to a bulged cube is obtained by
 *  interpolating between six surface maps, twelve bounding curves, and
 *  eight corners. The surface map for the upper +z axis is obtained by
 *  interpolating between a cubical surface and a spherical surface. The
 *  two surfaces are chosen such that the latter circumscribes the former.
 *
 *  We make a choice here as to whether we wish to use the logical coordinates
 *  parameterizing these surface as they are, in which case we have the
 *  equidistant choice of coordinates, or whether to apply a tangent map to them
 *  which leads us to the equiangular choice of coordinates. In terms of the
 *  logical coordinates, the equiangular coordinates are:
 *
 *  \f[\textrm{equiangular xi} : \Xi(\xi) = \textrm{tan}(\xi\pi/4)\f]
 *
 *  \f[\textrm{equiangular eta}  : \mathrm{H}(\eta) = \textrm{tan}(\eta\pi/4)\f]
 *
 *  With derivatives:
 *
 *  \f[\Xi'(\xi) = \frac{\pi}{4}(1+\Xi^2)\f]
 *
 *  \f[\mathrm{H}'(\eta) = \frac{\pi}{4}(1+\mathrm{H}^2)\f]
 *
 *  The equidistant coordinates are:
 *
 *  \f[ \textrm{equidistant xi}  : \Xi = \xi\f]
 *
 *  \f[ \textrm{equidistant eta}  : \mathrm{H} = \eta\f]
 *
 *  with derivatives:
 *
 *  \f$\Xi'(\xi) = 1\f$, and \f$\mathrm{H}'(\eta) = 1\f$
 *
 *  We also define the variable \f$\rho\f$, given by:
 *
 *  \f[\rho = \sqrt{1+\Xi^2+\mathrm{H}^2}\f]
 *
 *  ### The Spherical Face Map
 *  The surface map for the spherical face of radius \f$R\f$ lying in the
 *  \f$+z\f$
 *  direction in either choice of coordinates is then given by:
 *
 *  \f[
 *  \vec{\sigma}_{spherical}(\xi,\eta) =
 *  \begin{bmatrix}
 *  x(\xi,\eta)\\
 *  y(\xi,\eta)\\
 *  z(\xi,\eta)\\
 *  \end{bmatrix}  = \frac{R}{\rho}
 *  \begin{bmatrix}
 *  \Xi\\
 *  \mathrm{H}\\
 *  1\\
 *  \end{bmatrix}
 *  \f]
 *
 *  ### The Cubical Face Map
 *  The surface map for the cubical face of side length \f$2L\f$ lying in the
 *  \f$+z\f$ direction is given by:
 *
 *  \f[
 *  \vec{\sigma}_{cubical}(\xi,\eta) =
 *  \begin{bmatrix}
 *  x(\xi,\eta)\\
 *  y(\xi,\eta)\\
 *  L\\
 *  \end{bmatrix}  = L
 *  \begin{bmatrix}
 *  \Xi\\
 *  \mathrm{H}\\
 *  1\\
 *  \end{bmatrix}
 *  \f]
 *
 *  ### The Bulged Face Map
 *  To construct the bulged map we interpolate between a cubical face map of
 *  side length \f$2L\f$ and a spherical face map of radius \f$R\f$, with the
 *  interpolation parameter being \f$s\f$, the `sphericity`.
 *  The surface map for the bulged face lying in the \f$+z\f$ direction is then
 *  given by:
 *
 *  \f[
 *  \vec{\sigma}_{+\zeta}(\xi,\eta) = \left\{(1-s)L + \frac{sR}{\rho}\right\}
 *  \begin{bmatrix}
 *  \Xi\\
 *  \mathrm{H}\\
 *  1\\
 *  \end{bmatrix}
 *  \f]
 *
 *  This equation defines the upper-z map \f$\vec{\sigma}_{+\zeta}\f$, and we
 *  similarly define the other five surface maps \f$\vec{\sigma}_{+\eta}\f$,
 *  \f$\vec{\sigma}_{+\xi}\f$, and so on by appropriate rotations.
 *  We constrain L by demanding that the spherical face circumscribe the cube.
 *  With this condition, we have \f$L = R/\sqrt3\f$.
 *
 *  ### The General Formula for 3D Isoparametric Maps
 *  The general formula is given by Eq. 1 in section 2.1 of Hesthaven's paper
 *  "A Stable Penalty Method For The Compressible Navier-Stokes Equations III.
 *  Multidimensional Domain Decomposition Schemes" available
 *   here .
 *
 *  Hesthaven's formula is general in the degree of the shape functions used,
 *  so for our purposes we take the special case where the shape functions are
 *  linear in the interpolation variable, and define new variables accordingly.
 *  However, our interpolation variables do not necessarily have to be the
 *  logical coordinates themselves, though they often are. To make this
 *  distinction clear, we will define the new interpolation variables
 *  \f$\{\tilde{\xi},\tilde{\eta},\tilde{\zeta}\}\f$, which may either be the
 *  logical coordinates themselves or a invertible transformation of them. For
 *  the purposes of the bulged cube map, this transformation will be the same
 *  transformation that takes the logical coordinates into the equiangular
 *  coordinates. We will later see how this choice can lead to simplifications
 *  in the final map.
 *
 *  We define the following variables for
 *  \f$\alpha, \beta, \gamma \in\{\tilde{\xi},\tilde{\eta},\tilde{\zeta}\}\f$:
 *
 *  \f[
 *  f^{\pm}_{\alpha} = \frac{1}{2}(1\pm\alpha)\\
 *  f^{\pm\pm}_{\alpha \ \beta} = \frac{1}{4}(1\pm\alpha)(1\pm\beta)\\
 *  f^{\pm\pm\pm}_{\alpha \ \beta \ \gamma} =
 *  \frac{1}{8}(1\pm\alpha)(1\pm\beta)(1\pm\gamma)
 *  \f]
 *
 *  The formula involves six surfaces, which we will denote by
 *  \f$\vec{\sigma}\f$, twelve curves, denoted by \f$\vec{\Gamma}\f$, and eight
 *  vertices, denoted by \f$\vec{\pi}\f$, with the subscripts denoting which
 *  face(s) these objects belong to. The full volume map is given by:
 *
 *  \f{align*}
 *  \vec{x}(\xi,\eta,\zeta) = &
 *  f^{+}_{\tilde{\zeta}}\vec{\sigma}_{+\zeta}(\xi, \eta)+
 *  f^{-}_{\tilde{\zeta}}\vec{\sigma}_{-\zeta}(\xi, \eta)\\
 *  &+ f^{+}_{\tilde{\eta}}\vec{\sigma}_{+\eta}(\xi, \zeta)+
 *  f^{-}_{\tilde{\eta}}\vec{\sigma}_{-\eta}(\xi, \zeta)+
 *  f^{+}_{\tilde{\xi}}\vec{\sigma}_{+\xi}(\eta, \zeta)+
 *  f^{-}_{\tilde{\xi}}\vec{\sigma}_{-\xi}(\eta, \zeta)\\
 *  &- f^{++}_{\tilde{\xi} \ \tilde{\eta}}\vec{\Gamma}_{+\xi +\eta}(\zeta)-
 *  f^{-+}_{\tilde{\xi} \ \tilde{\eta}}\vec{\Gamma}_{-\xi +\eta}(\zeta)-
 *  f^{+-}_{\tilde{\xi} \ \tilde{\eta}}\vec{\Gamma}_{+\xi -\eta}(\zeta)-
 *  f^{--}_{\tilde{\xi} \ \tilde{\eta}}\vec{\Gamma}_{-\xi -\eta}(\zeta)\\
 *  &- f^{++}_{\tilde{\xi} \ \tilde{\zeta}}\vec{\Gamma}_{+\xi +\zeta}(\eta)-
 *  f^{-+}_{\tilde{\xi} \ \tilde{\zeta}}\vec{\Gamma}_{-\xi +\zeta}(\eta)-
 *  f^{+-}_{\tilde{\xi} \ \tilde{\zeta}}\vec{\Gamma}_{+\xi -\zeta}(\eta)-
 *  f^{--}_{\tilde{\xi} \ \tilde{\zeta}}\vec{\Gamma}_{-\xi -\zeta}(\eta)\\
 *  &- f^{++}_{\tilde{\eta} \ \tilde{\zeta}}\vec{\Gamma}_{+\eta +\zeta}(\xi)-
 *  f^{-+}_{\tilde{\eta} \ \tilde{\zeta}}\vec{\Gamma}_{-\eta +\zeta}(\xi)-
 *  f^{+-}_{\tilde{\eta} \ \tilde{\zeta}}\vec{\Gamma}_{+\eta -\zeta}(\xi)-
 *  f^{--}_{\tilde{\eta} \tilde{\zeta}}\vec{\Gamma}_{-\eta -\zeta}(\xi)\\
 *  &+ f^{+++}_{\tilde{\xi} \ \tilde{\eta} \ \tilde{\zeta}}\vec{\pi}_{+\xi +\eta
 * +\zeta}+ f^{-++}_{\tilde{\xi} \ \tilde{\eta} \ \tilde{\zeta}}\vec{\pi}_{-\xi
 * +\eta +\zeta}+ f^{+-+}_{\tilde{\xi} \ \tilde{\eta} \
 * \tilde{\zeta}}\vec{\pi}_{+\xi -\eta +\zeta}+
 *  f^{--+}_{\tilde{\xi} \ \tilde{\eta} \ \tilde{\zeta}}\vec{\pi}_{-\xi -\eta
 * +\zeta}\\
 *  &+ f^{++-}_{\tilde{\xi} \ \tilde{\eta} \ \tilde{\zeta}}\vec{\pi}_{+\xi +\eta
 * -\zeta}+ f^{-+-}_{\tilde{\xi} \ \tilde{\eta} \ \tilde{\zeta}}\vec{\pi}_{-\xi
 * +\eta -\zeta}+ f^{+--}_{\tilde{\xi} \ \tilde{\eta} \
 * \tilde{\zeta}}\vec{\pi}_{+\xi -\eta -\zeta}+ f^{---}_{\tilde{\xi} \
 * \tilde{\eta} \ \tilde{\zeta}}\vec{\pi}_{-\xi -\eta -\zeta} \f}
 *
 *
 *  ### The Special Case for Octahedral Symmetry
 *  The general formula is for the case in which there are six independently
 *  specified bounding surfaces. In our case, the surfaces are obtained by
 *  rotations and reflections of the upper-\f$\zeta\f$ face.
 *
 * We define the matrices corresponding to these transformations to be:
 *
 * \f[
 * S_{xy} =
 *  \begin{bmatrix}
 *  0 & 1 & 0\\
 *  1 & 0 & 0\\
 *  0 & 0 & 1\\
 *  \end{bmatrix},\
 *
 * S_{xz} =
 *  \begin{bmatrix}
 *  0 & 0 & 1\\
 *  0 & 1 & 0\\
 *  1 & 0 & 0\\
 *  \end{bmatrix},\
 *
 * S_{yz} =
 *  \begin{bmatrix}
 *  1 & 0 & 0\\
 *  0 & 0 & 1\\
 *  0 & 1 & 0\\
 *  \end{bmatrix}\f]
 *
 * \f[C_{zxy} =
 *  \begin{bmatrix}
 *  0 & 0 & 1\\
 *  1 & 0 & 0\\
 *  0 & 1 & 0\\
 *  \end{bmatrix},\
 *
 * C_{yzx} =
 *  \begin{bmatrix}
 *  0 & 1 & 0\\
 *  0 & 0 & 1\\
 *  1 & 0 & 0\\
 *  \end{bmatrix}\f]
 *
 * \f[N_{x} =
 *  \begin{bmatrix}
 *  -1 & 0 & 0\\
 *  0 & 1 & 0\\
 *  0 & 0 & 1\\
 *  \end{bmatrix},\
 *
 * N_{y} =
 *  \begin{bmatrix}
 *  1 & 0 & 0\\
 *  0 & -1 & 0\\
 *  0 & 0 & 1\\
 *  \end{bmatrix},\
 *
 * N_{z} =
 *  \begin{bmatrix}
 *  1 & 0 & 0\\
 *  0 & 1 & 0\\
 *  0 & 0 & -1\\
 *  \end{bmatrix}
 *  \f]
 *
 * The surface maps can now all be written in terms of
 * \f$\vec{\sigma}_{+\zeta}\f$ and these matrices:
 *
 * \f$\vec{\sigma}_{-\zeta}(\xi, \eta) = N_z\vec{\sigma}_{+\zeta}(\xi, \eta)\\
 * \vec{\sigma}_{+\eta}(\xi, \zeta) = S_{yz}\vec{\sigma}_{+\zeta}(\xi, \zeta)\\
 * \vec{\sigma}_{-\eta}(\xi, \zeta) = N_yS_{yz}\vec{\sigma}_{+\zeta}(\xi,
 * \zeta)\\
 * \vec{\sigma}_{+\xi}(\eta, \zeta) = C_{zxy}\vec{\sigma}_{+\zeta}(\eta,
 * \zeta)\\
 * \vec{\sigma}_{-\xi}(\eta, \zeta) = N_xC_{zyx}\vec{\sigma}_{+\zeta}(\eta,
 * \zeta)\f$
 *
 *
 * The four bounding curves \f$\vec{\Gamma}\f$ on the \f$+\zeta\f$ face are
 * given by:
 *
 *
 * \f$\vec{\Gamma}_{+\xi,+\zeta}(\eta) = \vec{\sigma}_{+\zeta}(+1,\eta)\\
 * \vec{\Gamma}_{-\xi,+\zeta}(\eta) = \vec{\sigma}_{+\zeta}(-1,\eta)
 * = N_x\vec{\sigma}_{+\zeta}(+1, \eta)\\
 * \vec{\Gamma}_{+\eta,+\zeta}(\xi) = \vec{\sigma}_{+\zeta}(\xi,+1)
 * = S_{xy}\vec{\sigma}_{+\zeta}(+1, \xi)\\
 * \vec{\Gamma}_{-\eta,+\zeta}(\xi) = \vec{\sigma}_{+\zeta}(\xi,-1)
 * = N_yS_{xy}\vec{\sigma}_{+\zeta}(+1,\xi)\f$
 *
 *
 * The bounding curves on the other surfaces can be obtained by transformations
 * on the \f$+\zeta\f$ face:
 *
 *
 * \f$\vec{\Gamma}_{+\xi,-\zeta}(\eta) = N_z\vec{\sigma}_{+\zeta}(+1,\eta)\\
 * \vec{\Gamma}_{-\xi,-\zeta}(\eta) = N_z\vec{\sigma}_{+\zeta}(-1,\eta)
 * = N_zN_x\vec{\sigma}_{+\zeta}(+1,\eta)\\
 * \vec{\Gamma}_{+\eta,-\zeta}(\xi) = N_z\vec{\sigma}_{+\zeta}(\xi,+1)
 * = N_zS_{xy}\vec{\sigma}_{+\zeta}(+1, \xi)\\
 * \vec{\Gamma}_{-\eta,-\zeta}(\xi) = N_z\vec{\sigma}_{+\zeta}(\xi,-1)
 * = N_zN_yS_{xy}\vec{\sigma}_{+\zeta}(+1, \xi)\\
 * \vec{\Gamma}_{+\xi,+\eta}(\zeta) =
 * C_{zxy}\vec{\sigma}_{+\zeta}(+1,\zeta)\\
 * \vec{\Gamma}_{-\xi,+\eta}(\zeta) =
 * N_xC_{zxy}\vec{\sigma}_{+\zeta}(+1,\zeta)\\
 * \vec{\Gamma}_{+\xi,-\eta}(\zeta) = C_{zxy}\vec{\sigma}_{+\zeta}(-1,\zeta)
 * = C_{zxy}N_x\vec{\sigma}_{+\zeta}(+1,\zeta)\\
 * \vec{\Gamma}_{-\xi,-\eta}(\zeta) = N_xC_{zxy}\vec{\sigma}_{+\zeta}(-1,\zeta)
 * = N_xC_{zxy}N_x\vec{\sigma}_{+\zeta}(+1,\zeta)\f$
 *
 *
 * Now we can write the volume map in terms of
 * \f$\vec{\sigma}_{+\zeta}\f$ only:
 * \f{align*}\vec{x}(\xi,\eta,\zeta) = &
 * (f^{+}_{\tilde{\zeta}} + f^{-}_{\tilde{\zeta}}N_z)
 * \vec{\sigma}_{+\zeta}(\xi, \eta)\\
 * &+ (f^{+}_{\tilde{\eta}} + f^{-}_{\tilde{\eta}}N_y)
 * S_{yz}\vec{\sigma}_{+\zeta}(\xi, \zeta)\\
 * &+ (f^{+}_{\tilde{\xi}} + f^{-}_{\tilde{\xi}}N_x)
 * C_{zxy}\vec{\sigma}_{+\zeta}(\eta, \zeta)\\
 * &- (f^{+}_{\tilde{\xi}}+f^{-}_{\tilde{\xi}}N_x)
 * (f^{+}_{\tilde{\eta}}+f^{-}_{\tilde{\eta}}N_y)
 * C_{zxy}\vec{\sigma}_{+\zeta}(+1, \zeta)\\
 * &- (f^{+}_{\tilde{\zeta}}+f^{-}_{\tilde{\zeta}}N_z)\left\{
 * (f^{+}_{\tilde{\xi}}+f^{-}_{\tilde{\xi}}N_x)\vec{\sigma}_{+\zeta}(+1, \eta)+
 * (f^{+}_{\tilde{\eta}}+f^{-}_{\tilde{\eta}}N_y)S_{xy}\vec{\sigma}_{+\zeta}(+1,
 * \xi)\right\}\\
 * &+ \frac{r}{\sqrt{3}}\vec{\tilde{\xi}}
 *  \f}
 *
 * Note that we can now absorb all of the \f$f\f$s into the matrix prefactors
 * in the above equation and obtain a final set of matrices. We define the
 * following *blending matrices*:
 *
 *  \f[
 *  B_{\tilde{\xi}} =
 *  \begin{bmatrix}
 *  0 & 0 & \tilde{\xi}\\
 *  1 & 0 & 0\\
 *  0 & 1 & 0\\
 *  \end{bmatrix},\
 *
 *  B_{\tilde{\eta}} =
 *  \begin{bmatrix}
 *  1 & 0 & 0\\
 *  0 & 0 & \tilde{\eta}\\
 *  0 & 1 & 0\\
 *  \end{bmatrix},\
 *
 *  B_{\tilde{\zeta}} =
 *  \begin{bmatrix}
 *  1 & 0 & 0\\
 *  0 & 1 & 0\\
 *  0 & 0 & \tilde{\zeta}\\
 *  \end{bmatrix}\\
 *
 *  B_{\tilde{\xi}\tilde{\eta}} =
 *  \begin{bmatrix}
 *  0 & 0 & \tilde{\xi}\\
 *  \tilde{\eta} & 0 & 0\\
 *  0 & 1 & 0\\
 *  \end{bmatrix},\
 *
 *  B_{\tilde{\xi}\tilde{\zeta}} =
 *  \begin{bmatrix}
 *  \tilde{\xi} & 0 & 0\\
 *  0 & 1 & 0\\
 *  0 & 0 & \tilde{\zeta}\\
 *  \end{bmatrix},\
 *
 *  B_{\tilde{\eta}\tilde{\zeta}} =
 *  \begin{bmatrix}
 *  0 & 1 & 0\\
 *  \tilde{\eta} & 0 & 0\\
 *  0 & 0 & \tilde{\zeta}\\
 *  \end{bmatrix}\\
 *
 *  B_{\tilde{\xi}\tilde{\eta}\tilde{\zeta}} =
 *  \begin{bmatrix}
 *  \tilde{\xi} & 0 & 0\\
 *  0 & \tilde{\eta} & 0\\
 *  0 & 0 & \tilde{\zeta}\\
 *  \end{bmatrix}
 *  \f]
 *
 *  Now we can write the volume map in these terms:
 *
 * \f{align*}
 * \vec{x}(\xi,\eta,\zeta) = &
 * B_{\tilde{\zeta}}
 * \vec{\sigma}_{+\zeta}(\xi, \eta)\\& +
 * B_{\tilde{\eta}}
 * \vec{\sigma}_{+\zeta}(\xi, \zeta)+
 * B_{\tilde{\xi}}
 * \vec{\sigma}_{+\zeta}(\eta, \zeta)\\& -
 * B_{\tilde{\xi} \tilde{\eta}}
 * \vec{\sigma}_{+\zeta}(+1, \zeta)-
 * B_{\tilde{\xi} \tilde{\zeta}}
 * \vec{\sigma}_{+\zeta}(+1, \eta)+
 * B_{\tilde{\eta} \tilde{\zeta}}
 * \vec{\sigma}_{+\zeta}(+1, \xi)\\& +
 * B_{\tilde{\xi} \tilde{\eta} \tilde{\zeta}}
 * \vec{\sigma}_{+\zeta}(+1, +1)
 * \f}
 *
 * ### The Bulged Cube Map
 * We now use the result above to provide the mapping for the bulged cube.
 * First we will define the variables \f$\rho_A\f$ and \f$\rho_{AB}\f$, for
 * \f$A, B \in \{\Xi,\mathrm{H}, \mathrm{Z}\} \f$, where \f$\mathrm{Z}\f$
 * is \f$\tan(\zeta\pi/4)\f$ in the equiangular case and \f$\zeta\f$ in the
 * equidistant case:
 *
 * \f[
 * \rho_A = \sqrt{2 + A^2}\\
 * \rho_{AB} = \sqrt{1 + A^2 + B^2}
 * \f]
 * The final mapping is then:
 * \f[
 * \vec{x}(\xi,\eta,\zeta) = \frac{(1-s)R}{\sqrt{3}}
 * \begin{bmatrix}
 * \Xi\\
 * \mathrm{H}\\
 * \mathrm{Z}\\
 * \end{bmatrix} +
 * \frac{sR}{\sqrt{3}}
 * \begin{bmatrix}
 * \tilde{\xi}\\
 * \tilde{\eta}\\
 * \tilde{\zeta}\\
 * \end{bmatrix} + sR
 * \begin{bmatrix}
 * \tilde{\xi} & \Xi & \Xi\\
 * \mathrm{H} & \tilde{\eta} &\mathrm{H}\\
 * \mathrm{Z} & \mathrm{Z} & \tilde{\zeta}\\
 * \end{bmatrix}
 * \begin{bmatrix}
 * 1/\rho_{\mathrm{H}\mathrm{Z}}\\
 * 1/\rho_{\Xi\mathrm{Z}}\\
 * 1/\rho_{\Xi\mathrm{H}}\\
 * \end{bmatrix} - sR
 * \begin{bmatrix}
 * \Xi & \tilde{\xi} & \tilde{\xi}\\
 * \tilde{\eta} & \mathrm{H} &\tilde{\eta}\\
 * \tilde{\zeta} & \tilde{\zeta} & \mathrm{Z}\\
 * \end{bmatrix}
 * \begin{bmatrix}
 * 1/\rho_{\Xi}\\
 * 1/\rho_{\mathrm{H}}\\
 * 1/\rho_{\mathrm{Z}}\\
 * \end{bmatrix}
 * \f]
 *
 * Recall that the lower case Greek letters with tildes are the variables
 * used for the linear interpolation between the six bounding surfaces, and
 * that the upper case Greek letters are the coordinates along these surfaces -
 * both of which can be specified to be either
 * equidistant or equiangular. In the case where the
 * interpolation variable is chosen to match that of the
 * coordinates along the surface, we have \f$\tilde{\xi} = \Xi\f$, etc. In this
 * case, the formula reduces further. The reduced formula below is the one used
 * for this CoordinateMap. It is given by:
 *
 * \f[
 * \vec{x}(\xi,\eta,\zeta) =
 * \left\{
 * \frac{R}{\sqrt{3}}
 * + sR
 * \left(
 * 1/\rho_{\mathrm{H}\mathrm{Z}}+
 * 1/\rho_{\Xi\mathrm{Z}}+
 * 1/\rho_{\Xi\mathrm{H}}-
 * 1/\rho_{\Xi}-
 * 1/\rho_{\mathrm{H}}-
 * 1/\rho_{\mathrm{Z}}
 * \right)
 * \right\}
 * \begin{bmatrix}
 * \Xi\\
 * \mathrm{H}\\
 * \mathrm{Z}\\
 * \end{bmatrix}
 * \f]
 *
 * The inverse mapping is analytic in the angular directions. A root find
 * must be performed for the inverse mapping in the radial direction. This
 * one-dimensional formula is obtained by taking the magnitude of both sides
 * of the mapping, and changing variables from \f$\xi, \eta, \zeta\f$ to
 * \f$x, y, z\f$ and introducing \f$\rho^2 := \sqrt{\xi^2+\eta^2+\zeta^2}\f$.
 */
class BulgedCube {
 public:
  static constexpr size_t dim = 3;
  BulgedCube(double radius, double sphericity,
             bool use_equiangular_map) noexcept;
  BulgedCube() noexcept = default;
  ~BulgedCube() noexcept = default;
  BulgedCube(BulgedCube&&) noexcept = default;
  BulgedCube(const BulgedCube&) noexcept = default;
  BulgedCube& operator=(const BulgedCube&) noexcept = default;
  BulgedCube& operator=(BulgedCube&&) noexcept = default;

  template <typename T>
  std::array<tt::remove_cvref_wrap_t<T>, 3> operator()(
      const std::array<T, 3>& source_coords) const noexcept;

  boost::optional<std::array<double, 3>> inverse(
      const std::array<double, 3>& target_coords) const noexcept;

  template <typename T>
  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian(
      const std::array<T, 3>& source_coords) const noexcept;

  template <typename T>
  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian(
      const std::array<T, 3>& source_coords) const noexcept;

  // clang-tidy: google runtime references
  void pup(PUP::er& p) noexcept;  // NOLINT

  bool is_identity() const noexcept { return is_identity_; }

 private:
  template <typename T>
  std::array<tt::remove_cvref_wrap_t<T>, 3> xi_derivative(
      const std::array<T, 3>& source_coords) const noexcept;
  friend bool operator==(const BulgedCube& lhs, const BulgedCube& rhs) noexcept;

  double radius_{std::numeric_limits<double>::signaling_NaN()};
  double sphericity_{std::numeric_limits<double>::signaling_NaN()};
  bool use_equiangular_map_ = false;
  bool is_identity_ = false;
};

bool operator!=(const BulgedCube& lhs, const BulgedCube& rhs) noexcept;
}  // namespace CoordinateMaps
}  // namespace domain


//  Copyright John Maddock 2006, 2007, 2012, 2014.
//  Copyright Paul A. Bristow 2006, 2007, 2012

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

// This file includes *all* the special functions.
// this may be useful if many are used
// - to avoid including each function individually.

#ifndef BOOST_MATH_SPECIAL_FUNCTIONS_HPP
#define BOOST_MATH_SPECIAL_FUNCTIONS_HPP

#include <boost/math/special_functions/airy.hpp>
#include <boost/math/special_functions/acosh.hpp>
#include <boost/math/special_functions/asinh.hpp>
#include <boost/math/special_functions/atanh.hpp>
#include <boost/math/special_functions/bernoulli.hpp>
#include <boost/math/special_functions/bessel.hpp>
#include <boost/math/special_functions/bessel_prime.hpp>
#include <boost/math/special_functions/beta.hpp>
#include <boost/math/special_functions/binomial.hpp>
#include <boost/math/special_functions/cbrt.hpp>
#include <boost/math/special_functions/cos_pi.hpp>
#include <boost/math/special_functions/chebyshev.hpp>
#include <boost/math/special_functions/digamma.hpp>
#include <boost/math/special_functions/ellint_1.hpp>
#include <boost/math/special_functions/ellint_2.hpp>
#include <boost/math/special_functions/ellint_3.hpp>
#include <boost/math/special_functions/ellint_d.hpp>
#include <boost/math/special_functions/jacobi_theta.hpp>
#include <boost/math/special_functions/jacobi_zeta.hpp>
#include <boost/math/special_functions/heuman_lambda.hpp>
#include <boost/math/special_functions/ellint_rc.hpp>
#include <boost/math/special_functions/ellint_rd.hpp>
#include <boost/math/special_functions/ellint_rf.hpp>
#include <boost/math/special_functions/ellint_rj.hpp>
#include <boost/math/special_functions/ellint_rg.hpp>
#include <boost/math/special_functions/erf.hpp>
#include <boost/math/special_functions/expint.hpp>
#include <boost/math/special_functions/expm1.hpp>
#include <boost/math/special_functions/factorials.hpp>
#include <boost/math/special_functions/fpclassify.hpp>
#include <boost/math/special_functions/gamma.hpp>
#include <boost/math/special_functions/hermite.hpp>
#include <boost/math/special_functions/hypot.hpp>
#include <boost/math/special_functions/hypergeometric_1F0.hpp>
#include <boost/math/special_functions/hypergeometric_0F1.hpp>
#include <boost/math/special_functions/hypergeometric_2F0.hpp>
#include <boost/math/special_functions/hypergeometric_1F1.hpp>
#include <boost/math/special_functions/hypergeometric_pFq.hpp>
#include <boost/math/special_functions/jacobi_elliptic.hpp>
#include <boost/math/special_functions/laguerre.hpp>
#include <boost/math/special_functions/lanczos.hpp>
#include <boost/math/special_functions/legendre.hpp>
#include <boost/math/special_functions/log1p.hpp>
#include <boost/math/special_functions/math_fwd.hpp>
#include <boost/math/special_functions/next.hpp>
#include <boost/math/special_functions/owens_t.hpp>
#include <boost/math/special_functions/polygamma.hpp>
#include <boost/math/special_functions/powm1.hpp>
#include <boost/math/special_functions/sign.hpp>
#include <boost/math/special_functions/sin_pi.hpp>
#include <boost/math/special_functions/sinc.hpp>
#include <boost/math/special_functions/sinhc.hpp>
#include <boost/math/special_functions/spherical_harmonic.hpp>
#include <boost/math/special_functions/sqrt1pm1.hpp>
#include <boost/math/special_functions/zeta.hpp>
#include <boost/math/special_functions/modf.hpp>
#include <boost/math/special_functions/round.hpp>
#include <boost/math/special_functions/trunc.hpp>
#include <boost/math/special_functions/pow.hpp>
#include <boost/math/special_functions/next.hpp>
#include <boost/math/special_functions/owens_t.hpp>
#include <boost/math/special_functions/hankel.hpp>
#include <boost/math/special_functions/ulp.hpp>
#include <boost/math/special_functions/relative_difference.hpp>
#include <boost/math/special_functions/lambert_w.hpp>
#include <boost/math/special_functions/gegenbauer.hpp>
#include <boost/math/special_functions/jacobi.hpp>
#include <boost/math/special_functions/legendre_stieltjes.hpp>
#endif // BOOST_MATH_SPECIAL_FUNCTIONS_HPP


#include <iomanip>
#include <iostream>

#include <boost/math/special_functions/bessel.hpp>
#include <boost/math/special_functions/cbrt.hpp>
#include <boost/math/special_functions/gamma.hpp>

#include <boost/math/bindings/e_float.hpp>
#include <boost/multiprecision/number.hpp>

#include "examples.h"

typedef boost::multiprecision::number<boost::math::ef::e_float,
                                      boost::multiprecision::et_off>
e_float_type;

void examples::nr_021::boost_bindings()
{
  const e_float_type y(char(1));
  const e_float_type x(3.0L);
  const e_float_type z(y / x);

  const e_float_type a(z);

  const e_float_type lg_max = log((std::numeric_limits<e_float_type>::max)());
  const e_float_type eps    = std::numeric_limits<e_float_type>::epsilon();

  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10) << e_float_type(0.5F)                 << std::endl;
  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10) << z                                  << std::endl;
  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10) << sin  (z)                           << std::endl;
  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10) << floor(e_float_type(1.5F))          << std::endl;
  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10) << ceil (e_float_type(1.5F))          << std::endl;
  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10) << sqrt (e_float_type(2))             << std::endl;
  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10) << boost::math::cbrt(e_float_type(2)) << std::endl;
  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10) << exp  (z)                           << std::endl;
  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10) << atan2(y, x)                        << std::endl;
  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10) << atan (z)                           << std::endl;
  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10) << log  (e_float_type(2))             << std::endl;
  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10) << lg_max                             << std::endl;

  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10) << eps                    << std::endl;
  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10) << sqrt(eps)              << std::endl;
  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10) << boost::math::cbrt(eps) << std::endl;

  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10) << boost::math::tgamma(e_float_type(0.5F))          << std::endl;
  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10) << sqrt(boost::math::constants::pi<e_float_type>()) << std::endl;

  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10) << boost::math::cyl_bessel_j(e_float_type(1) / 7, e_float_type(2.5F))  << std::endl;

  int nexp;
  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10) << frexp(e_float_type(2), &nexp) << std::endl;
  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10) << ldexp(e_float_type(1), 2)     << std::endl;

  std::cout << std::boolalpha << (x < y) << std::endl;
  std::cout << std::boolalpha << (y > x) << std::endl;

  std::cout << std::boolalpha << (lg_max > (std::numeric_limits<std::int64_t>::max)()) << std::endl;
  std::cout << std::boolalpha << (lg_max > (std::numeric_limits<std::int64_t>::min)()) << std::endl;

  const e_float_type a3(("33." + std::string(std::size_t(std::numeric_limits<e_float_type>::digits10 - 2), char('3')) + std::string(16U, char('4'))).c_str());
  const e_float_type b3(e_float_type(100U) / 3);

  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10 + 4) << a3 << std::endl;
  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10 + 4) << b3 << std::endl;

  std::cout << std::boolalpha << (a3 == b3) << std::endl;
  std::cout << std::boolalpha << (a3 >  b3) << std::endl;
  std::cout << std::boolalpha << (a3 <  b3) << std::endl;

  const e_float_type c3(("33." + std::string(120U, char('3'))).c_str());
  const e_float_type d3(e_float_type(100U) / 3);

  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10 + 4) << c3 << std::endl;
  std::cout << std::setprecision(std::numeric_limits<e_float_type>::digits10 + 4) << d3 << std::endl;

  std::cout << std::boolalpha << (c3 == d3) << std::endl;
  std::cout << std::boolalpha << (c3 >  d3) << std::endl;
  std::cout << std::boolalpha << (c3 <  d3) << std::endl;
}


#include <boost/test/unit_test.hpp>
#include <boost/optional/optional_io.hpp>

#include <iostream>

#include <smtrat-modules/PBPPModule/CardinalityEncoder.h>

using namespace smtrat;

struct CardinalityFixture {
    CardinalityFixture() { }

    CardinalityEncoder encoder;
};

BOOST_FIXTURE_TEST_SUITE( s, CardinalityFixture )

BOOST_AUTO_TEST_SUITE( CardinalityEncoder )
BOOST_AUTO_TEST_SUITE( Equality );

BOOST_AUTO_TEST_CASE( CardinalityEncoder_Single_Literal_False )
{
	carl::Variable x = carl::freshBooleanVariable("x");

	ConstraintT constraint = ConstraintT(Poly(x), carl::Relation::EQ);

	boost::optional<FormulaT> result = encoder.encode(constraint);

	if (!result) {
		BOOST_FAIL("result != {} expected, but got {}.");
	}

	FormulaT expected = FormulaT(carl::FormulaType::NOT, FormulaT(x));

	BOOST_TEST((expected == *result), "expected " << expected << " but got " << result);
}

BOOST_AUTO_TEST_CASE( CardinalityEncoder_Multi_Literal_False )
{
	carl::Variable x = carl::freshBooleanVariable("x");
	carl::Variable y = carl::freshBooleanVariable("x");
	carl::Variable z = carl::freshBooleanVariable("x");

	ConstraintT constraint = ConstraintT(Poly(x) + Poly(y) + Poly(z), carl::Relation::LEQ);

	boost::optional<FormulaT> result = encoder.encode(constraint);

	if (!result) {
		BOOST_FAIL("result != {} expected, but got {}.");
	}

	FormulaT expected = FormulaT(carl::FormulaType::AND, !FormulaT(x), !FormulaT(y), !FormulaT(z));

	BOOST_TEST((expected == *result), "expected " << expected << " but got " << result);
}

BOOST_AUTO_TEST_CASE( CardinalityEncoder_Simple_EQ )
{
	carl::Variable x = carl::freshBooleanVariable("x");
	carl::Variable y = carl::freshBooleanVariable("y");

	ConstraintT constraint = ConstraintT(Poly(x) + Poly(y) + Rational(-1), carl::Relation::EQ);

	boost::optional<FormulaT> result = encoder.encode(constraint);

	if (!result) {
		BOOST_FAIL("result != {} expected, but got {}.");
	}

	FormulaT expected = FormulaT(carl::FormulaType::OR, FormulaT(carl::FormulaType::AND, FormulaT(x), !FormulaT(y)), FormulaT(carl::FormulaType::AND, !FormulaT(x), FormulaT(y)));

	BOOST_TEST((expected == *result), "expected " << expected << " but got " << result);
}

BOOST_AUTO_TEST_CASE( CardinalityEncoder_Simple_EQ_3_1 )
{
	carl::Variable x = carl::freshBooleanVariable("x");
	carl::Variable y = carl::freshBooleanVariable("y");
	carl::Variable z = carl::freshBooleanVariable("z");

	ConstraintT constraint = ConstraintT(Poly(x) + Poly(y) + Poly(z) + Rational(-1), carl::Relation::EQ);

	boost::optional<FormulaT> result = encoder.encode(constraint);

	if (!result) {
		BOOST_FAIL("result != {} expected, but got {}.");
	}

	FormulaT expected = FormulaT(carl::FormulaType::OR,
			FormulaT(carl::FormulaType::AND, !FormulaT(x), !FormulaT(y), FormulaT(z)),
			FormulaT(carl::FormulaType::AND, FormulaT(x), !FormulaT(y), !FormulaT(z)),
			FormulaT(carl::FormulaType::AND, !FormulaT(x), FormulaT(y), !FormulaT(z)));

	BOOST_TEST((expected == *result), "expected " << expected << " but got " << result);
}

BOOST_AUTO_TEST_CASE( CardinalityEncoder_Simple_EQ_3_2 )
{
	carl::Variable x = carl::freshBooleanVariable("x");
	carl::Variable y = carl::freshBooleanVariable("y");
	carl::Variable z = carl::freshBooleanVariable("z");

	ConstraintT constraint = ConstraintT(Poly(x) + Poly(y) + Poly(z) + Rational(-2), carl::Relation::EQ);

	boost::optional<FormulaT> result = encoder.encode(constraint);

	if (!result) {
		BOOST_FAIL("result != {} expected, but got {}.");
	}

	FormulaT expected = FormulaT(carl::FormulaType::OR,
			FormulaT(carl::FormulaType::AND, !FormulaT(x), FormulaT(y), FormulaT(z)),
			FormulaT(carl::FormulaType::AND, FormulaT(x), !FormulaT(y), FormulaT(z)),
			FormulaT(carl::FormulaType::AND, FormulaT(x), FormulaT(y), !FormulaT(z)));

	BOOST_TEST((expected == *result), "expected " << expected << " but got " << result);
}

BOOST_AUTO_TEST_CASE( CardinalityEncoder_Simple_EQ_3_2_Negative )
{
	carl::Variable x = carl::freshBooleanVariable("x");
	carl::Variable y = carl::freshBooleanVariable("y");
	carl::Variable z = carl::freshBooleanVariable("z");

	ConstraintT constraint = ConstraintT(-Poly(x) - Poly(y) - Poly(z) - Rational(-2), carl::Relation::EQ);

	boost::optional<FormulaT> result = encoder.encode(constraint);

	if (!result) {
		BOOST_FAIL("result != {} expected, but got {}.");
	}

	FormulaT expected = FormulaT(carl::FormulaType::OR,
			FormulaT(carl::FormulaType::AND, !FormulaT(x), FormulaT(y), FormulaT(z)),
			FormulaT(carl::FormulaType::AND, FormulaT(x), !FormulaT(y), FormulaT(z)),
			FormulaT(carl::FormulaType::AND, FormulaT(x), FormulaT(y), !FormulaT(z)));

	BOOST_TEST((expected == *result), "expected " << expected << " but got " << result);
}

BOOST_AUTO_TEST_CASE( CardinalityEncoder_Simple_EQ_FALSE )
{
	carl::Variable x = carl::freshBooleanVariable("x");
	carl::Variable y = carl::freshBooleanVariable("y");
	carl::Variable z = carl::freshBooleanVariable("z");

	ConstraintT constraint = ConstraintT(-Poly(x) - Poly(y) - Poly(z) - Rational(-4), carl::Relation::EQ);

	boost::optional<FormulaT> result = encoder.encode(constraint);

	if (!result) {
		BOOST_FAIL("result != {} expected, but got {}.");
	}

	FormulaT expected = FormulaT(carl::FormulaType::FALSE);

	BOOST_TEST((expected == *result), "expected " << expected << " but got " << result);
}

// END TEST SUITE EQ
BOOST_AUTO_TEST_SUITE_END();

BOOST_AUTO_TEST_CASE( CardinalityEncoder_Simple_LEQ_Coeff_LESS_CONST )
{
	carl::Variable x = carl::freshBooleanVariable("x");
	carl::Variable y = carl::freshBooleanVariable("y");
	carl::Variable z = carl::freshBooleanVariable("z");

	ConstraintT constraint = ConstraintT(Poly(x) + Poly(y) + Poly(z) - Rational(4), carl::Relation::LEQ);

	boost::optional<FormulaT> result = encoder.encode(constraint);

	if (!result) {
		BOOST_FAIL("result != {} expected, but got {}.");
	}

	// since 0 <= 4 is of course true as well
	FormulaT expected = FormulaT(carl::FormulaType::TRUE);

	BOOST_TEST((expected == *result), "expected " << expected << " but got " << result);
}

BOOST_AUTO_TEST_CASE(CardinalityEncoder_Simple_LEQ)
{
	carl::Variable x = carl::freshBooleanVariable("x");
	carl::Variable y = carl::freshBooleanVariable("y");

	ConstraintT constraint = ConstraintT(Poly(x) + Poly(y) + Rational(-1), carl::Relation::LEQ);

	boost::optional<FormulaT> result = encoder.encode(constraint);

	if (!result) {
		BOOST_FAIL("result != {} expected, but got {}.");
	}

	FormulaT expected = FormulaT(carl::FormulaType::OR,
			FormulaT(carl::FormulaType::AND, !FormulaT(x), !FormulaT(y)),
			FormulaT(carl::FormulaType::AND, !FormulaT(x), FormulaT(y)),
			FormulaT(carl::FormulaType::AND, FormulaT(x), !FormulaT(y)));

	BOOST_TEST((expected == *result), "expected " << expected << " but got " << result);
}

BOOST_AUTO_TEST_CASE(CardinalityEncoder_PositiveCoeff_AtMost)
{
	carl::Variable x1 = carl::freshBooleanVariable("x1");
	carl::Variable x2 = carl::freshBooleanVariable("x2");
	carl::Variable x3 = carl::freshBooleanVariable("x3");

	// x1 + x2 + x3 + x4 <= 2
	ConstraintT constraint = ConstraintT(Poly(x1) + Poly(x2) + Poly(x3) + Rational(-2), carl::Relation::LEQ);

	boost::optional<FormulaT> result = encoder.encode(constraint);

	if (!result) {
		BOOST_FAIL("result != {} expected, but got {}.");
	}

	FormulasT conjunctions;
	// none
	conjunctions.push_back(FormulaT(carl::FormulaType::AND, !FormulaT(x1), !FormulaT(x2), !FormulaT(x3)));
	// one
	conjunctions.push_back(FormulaT(carl::FormulaType::AND, FormulaT(x1), !FormulaT(x2), !FormulaT(x3)));
	conjunctions.push_back(FormulaT(carl::FormulaType::AND, !FormulaT(x1), FormulaT(x2), !FormulaT(x3)));
	conjunctions.push_back(FormulaT(carl::FormulaType::AND, !FormulaT(x1), !FormulaT(x2), FormulaT(x3)));
	// two
	conjunctions.push_back(FormulaT(carl::FormulaType::AND, FormulaT(x1), FormulaT(x2), !FormulaT(x3)));
	conjunctions.push_back(FormulaT(carl::FormulaType::AND, FormulaT(x1), !FormulaT(x2), FormulaT(x3)));
	conjunctions.push_back(FormulaT(carl::FormulaType::AND, !FormulaT(x1), FormulaT(x2), FormulaT(x3)));

	FormulaT expected = FormulaT(carl::FormulaType::OR, conjunctions);

	BOOST_TEST((expected == *result), "expected " << expected << " but got " << result);
}

BOOST_AUTO_TEST_CASE(CardinalityEncoder_PositiveCoeff_AtMost_Strict)
{
	carl::Variable x1 = carl::freshBooleanVariable("x1");
	carl::Variable x2 = carl::freshBooleanVariable("x2");
	carl::Variable x3 = carl::freshBooleanVariable("x3");

	// x1 + x2 + x3 < 3
	ConstraintT constraint = ConstraintT(Poly(x1) + Poly(x2) + Poly(x3) + Rational(-3), carl::Relation::LESS);

	boost::optional<FormulaT> result = encoder.encode(constraint);

	if (!result) {
		BOOST_FAIL("result != {} expected, but got {}.");
	}

	FormulasT conjunctions;
	conjunctions.push_back(FormulaT(carl::FormulaType::AND, !FormulaT(x1), !FormulaT(x2), !FormulaT(x3)));
	conjunctions.push_back(FormulaT(carl::FormulaType::AND, FormulaT(x1), !FormulaT(x2), !FormulaT(x3)));
	conjunctions.push_back(FormulaT(carl::FormulaType::AND, !FormulaT(x1), FormulaT(x2), !FormulaT(x3)));
	conjunctions.push_back(FormulaT(carl::FormulaType::AND, !FormulaT(x1), !FormulaT(x2), FormulaT(x3)));
	conjunctions.push_back(FormulaT(carl::FormulaType::AND, FormulaT(x1), FormulaT(x2), !FormulaT(x3)));
	conjunctions.push_back(FormulaT(carl::FormulaType::AND, FormulaT(x1), !FormulaT(x2), FormulaT(x3)));
	conjunctions.push_back(FormulaT(carl::FormulaType::AND, !FormulaT(x1), FormulaT(x2), FormulaT(x3)));

	FormulaT expected = FormulaT(carl::FormulaType::OR, conjunctions);

	BOOST_TEST((expected == *result), "expected " << expected << " but got " << result);
}

BOOST_AUTO_TEST_CASE(CardinalityEncoder_PositiveCoeff_AtMost_True)
{
	carl::Variable x1 = carl::freshBooleanVariable("x1");
	carl::Variable x2 = carl::freshBooleanVariable("x2");
	carl::Variable x3 = carl::freshBooleanVariable("x3");
	carl::Variable x4 = carl::freshBooleanVariable("x4");

	// x1 + x2 + x3 + x4 <= 10
	ConstraintT constraint = ConstraintT(Poly(x1) + Poly(x2) + Poly(x3) + Poly(x4) + Rational(-10), carl::Relation::LEQ);

	boost::optional<FormulaT> result = encoder.encode(constraint);

	if (!result) {
		BOOST_FAIL("result != {} expected, but got {}.");
	}

	FormulaT expected = FormulaT(carl::FormulaType::TRUE);

	BOOST_TEST((expected == *result), "expected " << expected << " but got " << result);
}

BOOST_AUTO_TEST_CASE(CardinalityEncoder_PositiveCoeff_AtMost_False)
{
	carl::Variable x1 = carl::freshBooleanVariable("x1");
	carl::Variable x2 = carl::freshBooleanVariable("x2");
	carl::Variable x3 = carl::freshBooleanVariable("x3");
	carl::Variable x4 = carl::freshBooleanVariable("x4");

	// x1 + x2 + x3 + x4 <= -1
	ConstraintT constraint = ConstraintT(Poly(x1) + Poly(x2) + Poly(x3) + Poly(x4) + Rational(1), carl::Relation::LEQ);

	boost::optional<FormulaT> result = encoder.encode(constraint);

	if (!result) {
		BOOST_FAIL("result != {} expected, but got {}.");
	}

	FormulaT expected = FormulaT(carl::FormulaType::FALSE);

	BOOST_TEST((expected == *result), "expected " << expected << " but got " << result);
}

BOOST_AUTO_TEST_CASE(CardinalityEncoder_AtLeast_False)
{
	carl::Variable x1 = carl::freshBooleanVariable("x1");
	carl::Variable x2 = carl::freshBooleanVariable("x2");
	carl::Variable x3 = carl::freshBooleanVariable("x3");

	ConstraintT constraint = ConstraintT(-Poly(x1) - Poly(x2) - Poly(x3) - Rational(-4), carl::Relation::LEQ);

	boost::optional<FormulaT> result = encoder.encode(constraint);

	if (!result) {
		BOOST_FAIL("result != {} expected, but got {}.");
	}

	FormulaT expected = FormulaT(carl::FormulaType::FALSE);

	BOOST_TEST((expected == *result), "expected " << expected << " but got " << result);
}

BOOST_AUTO_TEST_CASE(CardinalityEncoder_AtLeast)
{
	carl::Variable x1 = carl::freshBooleanVariable("x1");
	carl::Variable x2 = carl::freshBooleanVariable("x2");
	carl::Variable x3 = carl::freshBooleanVariable("x3");

	ConstraintT constraint = ConstraintT(-Poly(x1) - Poly(x2) - Poly(x3) - Rational(-1), carl::Relation::LEQ);

	boost::optional<FormulaT> result = encoder.encode(constraint);

	if (!result) {
		BOOST_FAIL("result != {} expected, but got {}.");
	}

	FormulaT expected = FormulaT(carl::FormulaType::OR,
			FormulaT(x1),
			FormulaT(x2),
			FormulaT(x3));

	BOOST_TEST((expected == *result), "expected " << expected << " but got " << result);
}

BOOST_AUTO_TEST_CASE(CardinalityEncoder_AtLeast_2)
{
	carl::Variable x1 = carl::freshBooleanVariable("x1");
	carl::Variable x2 = carl::freshBooleanVariable("x2");
	carl::Variable x3 = carl::freshBooleanVariable("x3");

	ConstraintT constraint = ConstraintT(-Poly(x1) - Poly(x2) - Poly(x3) - Rational(-2), carl::Relation::LEQ);

	boost::optional<FormulaT> result = encoder.encode(constraint);

	if (!result) {
		BOOST_FAIL("result != {} expected, but got {}.");
	}

	FormulaT expected = FormulaT(carl::FormulaType::AND,
			// anything but at least one
			FormulaT(carl::FormulaType::OR, FormulaT(x1), FormulaT(x2), FormulaT(x3)),
			// and not not exactly 1
			!FormulaT(carl::FormulaType::OR,
				FormulaT(carl::FormulaType::AND, FormulaT(x1), !FormulaT(x2), !FormulaT(x3)),
				FormulaT(carl::FormulaType::AND, !FormulaT(x1), FormulaT(x2), !FormulaT(x3)),
				FormulaT(carl::FormulaType::AND, !FormulaT(x1), !FormulaT(x2), FormulaT(x3)))
			);

	BOOST_TEST((expected == *result), "expected " << expected << " but got " << result);
}

BOOST_AUTO_TEST_SUITE_END();
BOOST_AUTO_TEST_SUITE_END();