#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>

#include <boost/mpl/vector.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/quote.hpp>
#include <boost/mpl/protect.hpp>
#include <boost/mpl/bind.hpp>
#include <boost/mpl/list.hpp>

#include <cmath>
#include <limits>

#include <boost/safe_float.hpp>
#include <boost/safe_float/convenience.hpp>
#include <boost/safe_float/policy/cast_to_primitive.hpp>

//types to be tested
using test_types=boost::mpl::list<
    float, double, long double
>;

using namespace boost::safe_float;

/**
  This test suite checks the methods providing casting.
  */

BOOST_AUTO_TEST_SUITE( safe_float_casting_test_suite )

BOOST_AUTO_TEST_CASE_TEMPLATE( safe_float_get_stored_value, FPT, test_types){
    //set initial values
    FPT a = std::numeric_limits<FPT>::max();
    FPT b = std::numeric_limits<FPT>::min();
    FPT c = std::numeric_limits<FPT>::infinity();
    FPT d = 0;
    FPT e = 1;
    //create safe version of the values
    safe_float<FPT> sa(std::numeric_limits<FPT>::max());
    safe_float<FPT> sb(std::numeric_limits<FPT>::min());
    safe_float<FPT> sc(std::numeric_limits<FPT>::infinity());
    safe_float<FPT> sd(d);
    safe_float<FPT> se(e);
    //check the values match when obtaining the stored value
    BOOST_CHECK_EQUAL(a, sa.get_stored_value());
    BOOST_CHECK_EQUAL(b, sb.get_stored_value());
    BOOST_CHECK_EQUAL(c, sc.get_stored_value());
    BOOST_CHECK_EQUAL(d, sd.get_stored_value());
    BOOST_CHECK_EQUAL(e, se.get_stored_value());
}

template<typename FP, template<typename> typename CAST>
using sf_test_cast = safe_float<FP, policy::check_all, policy::on_fail_throw, CAST>;

BOOST_AUTO_TEST_CASE(safe_float_cast_from_primitive_none) {
    // Cannot be converted from any primitive but is explicitly constructible from float
    using sf = sf_test_cast<float, policy::cast_from_primitive::none>;

    BOOST_CHECK((not std::is_convertible_v<float, sf>));
    BOOST_CHECK((not std::is_convertible_v<double, sf>));
    BOOST_CHECK((not std::is_convertible_v<long double, sf>));
    BOOST_CHECK((std::is_constructible_v<sf, float>));
    BOOST_CHECK((not std::is_constructible_v<sf, double>));
    BOOST_CHECK((not std::is_constructible_v<sf, long double>));

    // Cannot be converted from any primitive but is explicitly constructible from double
    using sd = sf_test_cast<double, policy::cast_from_primitive::none>;

    BOOST_CHECK((not std::is_convertible_v<float, sd>));
    BOOST_CHECK((not std::is_convertible_v<double, sd>));
    BOOST_CHECK((not std::is_convertible_v<long double, sd>));
    BOOST_CHECK((not std::is_constructible_v<sd, float>));
    BOOST_CHECK((std::is_constructible_v<sd, double>));
    BOOST_CHECK((not std::is_constructible_v<sd, long double>));

    // Cannot be converted from any primitive but is explicitly constructible from long double
    using sld = sf_test_cast<long double, policy::cast_from_primitive::none>;

    BOOST_CHECK((not std::is_convertible_v<float, sld>));
    BOOST_CHECK((not std::is_convertible_v<double, sld>));
    BOOST_CHECK((not std::is_convertible_v<long double, sld>));
    BOOST_CHECK((not std::is_constructible_v<sld, float>));
    BOOST_CHECK((not std::is_constructible_v<sld, double>));
    BOOST_CHECK((std::is_constructible_v<sld, long double>));
}

BOOST_AUTO_TEST_CASE(safe_float_cast_from_primitive_none_explicit) {
    // Cannot be converted from any primitive but is explicitly constructible from float
    using sf = sf_test_cast<float, policy::cast_from_primitive::none_explicit>;

    BOOST_CHECK((not std::is_convertible_v<float, sf>));
    BOOST_CHECK((not std::is_convertible_v<double, sf>));
    BOOST_CHECK((not std::is_convertible_v<long double, sf>));
    BOOST_CHECK((std::is_constructible_v<sf, float>));
    BOOST_CHECK((not std::is_constructible_v<sf, double>));
    BOOST_CHECK((not std::is_constructible_v<sf, long double>));

    // Cannot be converted from any primitive but is explicitly constructible from double
    using sd = sf_test_cast<double, policy::cast_from_primitive::none_explicit>;

    BOOST_CHECK((not std::is_convertible_v<float, sd>));
    BOOST_CHECK((not std::is_convertible_v<double, sd>));
    BOOST_CHECK((not std::is_convertible_v<long double, sd>));
    BOOST_CHECK((not std::is_constructible_v<sd, float>));
    BOOST_CHECK((std::is_constructible_v<sd, double>));
    BOOST_CHECK((not std::is_constructible_v<sd, long double>));

    // Cannot be converted from any primitive but is explicitly constructible from long double
    using sld = sf_test_cast<long double, policy::cast_from_primitive::none_explicit>;

    BOOST_CHECK((not std::is_convertible_v<float, sld>));
    BOOST_CHECK((not std::is_convertible_v<double, sld>));
    BOOST_CHECK((not std::is_convertible_v<long double, sld>));
    BOOST_CHECK((not std::is_constructible_v<sld, float>));
    BOOST_CHECK((not std::is_constructible_v<sld, double>));
    BOOST_CHECK((std::is_constructible_v<sld, long double>));
}

BOOST_AUTO_TEST_CASE(safe_float_cast_from_primitive_same) {
    // Can be converted from float only
    using sf = sf_test_cast<float, policy::cast_from_primitive::same>;

    BOOST_CHECK((std::is_convertible_v<float, sf>));
    BOOST_CHECK((not std::is_convertible_v<double, sf>));
    BOOST_CHECK((not std::is_convertible_v<long double, sf>));
    BOOST_CHECK((std::is_constructible_v<sf, float>));
    BOOST_CHECK((not std::is_constructible_v<sf, double>));
    BOOST_CHECK((not std::is_constructible_v<sf, long double>));

    // Can be converted from double only
    using sd = sf_test_cast<double, policy::cast_from_primitive::same>;

    BOOST_CHECK((not std::is_convertible_v<float, sd>));
    BOOST_CHECK((std::is_convertible_v<double, sd>));
    BOOST_CHECK((not std::is_convertible_v<long double, sd>));
    BOOST_CHECK((not std::is_constructible_v<sd, float>));
    BOOST_CHECK((std::is_constructible_v<sd, double>));
    BOOST_CHECK((not std::is_constructible_v<sd, long double>));

    // Can be converted from long double only
    using sld = sf_test_cast<long double, policy::cast_from_primitive::same>;

    BOOST_CHECK((not std::is_convertible_v<float, sld>));
    BOOST_CHECK((not std::is_convertible_v<double, sld>));
    BOOST_CHECK((std::is_convertible_v<long double, sld>));
    BOOST_CHECK((not std::is_constructible_v<sld, float>));
    BOOST_CHECK((not std::is_constructible_v<sld, double>));
    BOOST_CHECK((std::is_constructible_v<sld, long double>));
}

BOOST_AUTO_TEST_CASE(safe_float_cast_from_primitive_same_explicit) {
    // Cannot be converted from any primitive but is explicitly constructible from float
    using sf = sf_test_cast<float, policy::cast_from_primitive::same_explicit>;

    BOOST_CHECK((not std::is_convertible_v<float, sf>));
    BOOST_CHECK((not std::is_convertible_v<double, sf>));
    BOOST_CHECK((not std::is_convertible_v<long double, sf>));
    BOOST_CHECK((std::is_constructible_v<sf, float>));
    BOOST_CHECK((not std::is_constructible_v<sf, double>));
    BOOST_CHECK((not std::is_constructible_v<sf, long double>));

    // Cannot be converted from any primitive but is explicitly constructible from double
    using sd = sf_test_cast<double, policy::cast_from_primitive::same_explicit>;

    BOOST_CHECK((not std::is_convertible_v<float, sd>));
    BOOST_CHECK((not std::is_convertible_v<double, sd>));
    BOOST_CHECK((not std::is_convertible_v<long double, sd>));
    BOOST_CHECK((not std::is_constructible_v<sd, float>));
    BOOST_CHECK((std::is_constructible_v<sd, double>));
    BOOST_CHECK((not std::is_constructible_v<sd, long double>));

    // Cannot be converted from any primitive but is explicitly constructible from long double
    using sld = sf_test_cast<long double, policy::cast_from_primitive::same_explicit>;

    BOOST_CHECK((not std::is_convertible_v<float, sld>));
    BOOST_CHECK((not std::is_convertible_v<double, sld>));
    BOOST_CHECK((not std::is_convertible_v<long double, sld>));
    BOOST_CHECK((not std::is_constructible_v<sld, float>));
    BOOST_CHECK((not std::is_constructible_v<sld, double>));
    BOOST_CHECK((std::is_constructible_v<sld, long double>));
}

BOOST_AUTO_TEST_CASE(safe_float_cast_from_primitive_more_precise) {
    // Can be converted from float, double and long double
    using sf = sf_test_cast<float, policy::cast_from_primitive::more_precise>;

    BOOST_CHECK((std::is_convertible_v<float, sf>));
    BOOST_CHECK((std::is_convertible_v<double, sf>));
    BOOST_CHECK((std::is_convertible_v<long double, sf>));
    BOOST_CHECK((std::is_constructible_v<sf, float>));
    BOOST_CHECK((std::is_constructible_v<sf, double>));
    BOOST_CHECK((std::is_constructible_v<sf, long double>));

    // Can be converted from double and long double
    using sd = sf_test_cast<double, policy::cast_from_primitive::more_precise>;

    BOOST_CHECK((not std::is_convertible_v<float, sd>));
    BOOST_CHECK((std::is_convertible_v<double, sd>));
    BOOST_CHECK((std::is_convertible_v<long double, sd>));
    BOOST_CHECK((not std::is_constructible_v<sd, float>));
    BOOST_CHECK((std::is_constructible_v<sd, double>));
    BOOST_CHECK((std::is_constructible_v<sd, long double>));

    // Can be converted from long double only
    using sld = sf_test_cast<long double, policy::cast_from_primitive::more_precise>;

    BOOST_CHECK((not std::is_convertible_v<float, sld>));
    BOOST_CHECK((not std::is_convertible_v<double, sld>));
    BOOST_CHECK((std::is_convertible_v<long double, sld>));
    BOOST_CHECK((not std::is_constructible_v<sld, float>));
    BOOST_CHECK((not std::is_constructible_v<sld, double>));
    BOOST_CHECK((std::is_constructible_v<sld, long double>));
}

BOOST_AUTO_TEST_CASE(safe_float_cast_from_primitive_more_precise_explicit) {
    // Can be explicitly constructed from float, double and long double
    using sf = sf_test_cast<float, policy::cast_from_primitive::more_precise_explicit>;

    BOOST_CHECK((not std::is_convertible_v<float, sf>));
    BOOST_CHECK((not std::is_convertible_v<double, sf>));
    BOOST_CHECK((not std::is_convertible_v<long double, sf>));
    BOOST_CHECK((std::is_constructible_v<sf, float>));
    BOOST_CHECK((std::is_constructible_v<sf, double>));
    BOOST_CHECK((std::is_constructible_v<sf, long double>));

    // Can be explicitly constructed from double and long double
    using sd = sf_test_cast<double, policy::cast_from_primitive::more_precise_explicit>;

    BOOST_CHECK((not std::is_convertible_v<float, sd>));
    BOOST_CHECK((not std::is_convertible_v<double, sd>));
    BOOST_CHECK((not std::is_convertible_v<long double, sd>));
    BOOST_CHECK((not std::is_constructible_v<sd, float>));
    BOOST_CHECK((std::is_constructible_v<sd, double>));
    BOOST_CHECK((std::is_constructible_v<sd, long double>));

    // Can be explicitly constructed from long double only
    using sld = sf_test_cast<long double, policy::cast_from_primitive::more_precise_explicit>;

    BOOST_CHECK((not std::is_convertible_v<float, sld>));
    BOOST_CHECK((not std::is_convertible_v<double, sld>));
    BOOST_CHECK((not std::is_convertible_v<long double, sld>));
    BOOST_CHECK((not std::is_constructible_v<sld, float>));
    BOOST_CHECK((not std::is_constructible_v<sld, double>));
    BOOST_CHECK((std::is_constructible_v<sld, long double>));
}

BOOST_AUTO_TEST_CASE(safe_float_cast_from_primitive_less_precise) {
    // Can be converted from float
    using sf = sf_test_cast<float, policy::cast_from_primitive::less_precise>;

    BOOST_CHECK((std::is_convertible_v<float, sf>));
    BOOST_CHECK((not std::is_convertible_v<double, sf>));
    BOOST_CHECK((not std::is_convertible_v<long double, sf>));
    BOOST_CHECK((std::is_constructible_v<sf, float>));
    BOOST_CHECK((not std::is_constructible_v<sf, double>));
    BOOST_CHECK((not std::is_constructible_v<sf, long double>));

    // Can be converted from float and double
    using sd = sf_test_cast<double, policy::cast_from_primitive::less_precise>;

    BOOST_CHECK((std::is_convertible_v<float, sd>));
    BOOST_CHECK((std::is_convertible_v<double, sd>));
    BOOST_CHECK((not std::is_convertible_v<long double, sd>));
    BOOST_CHECK((std::is_constructible_v<sd, float>));
    BOOST_CHECK((std::is_constructible_v<sd, double>));
    BOOST_CHECK((not std::is_constructible_v<sd, long double>));

    // Can be converted from float, double and long double
    using sld = sf_test_cast<long double, policy::cast_from_primitive::less_precise>;

    BOOST_CHECK((std::is_convertible_v<float, sld>));
    BOOST_CHECK((std::is_convertible_v<double, sld>));
    BOOST_CHECK((std::is_convertible_v<long double, sld>));
    BOOST_CHECK((std::is_constructible_v<sld, float>));
    BOOST_CHECK((std::is_constructible_v<sld, double>));
    BOOST_CHECK((std::is_constructible_v<sld, long double>));
}

BOOST_AUTO_TEST_CASE(safe_float_cast_from_primitive_less_precise_explicit) {
    // Can be explicitly constructed from float
    using sf = sf_test_cast<float, policy::cast_from_primitive::less_precise_explicit>;

    BOOST_CHECK((not std::is_convertible_v<float, sf>));
    BOOST_CHECK((not std::is_convertible_v<double, sf>));
    BOOST_CHECK((not std::is_convertible_v<long double, sf>));
    BOOST_CHECK((std::is_constructible_v<sf, float>));
    BOOST_CHECK((not std::is_constructible_v<sf, double>));
    BOOST_CHECK((not std::is_constructible_v<sf, long double>));

    // Can be explicitly constructed from float and double
    using sd = sf_test_cast<double, policy::cast_from_primitive::less_precise_explicit>;

    BOOST_CHECK((not std::is_convertible_v<float, sd>));
    BOOST_CHECK((not std::is_convertible_v<double, sd>));
    BOOST_CHECK((not std::is_convertible_v<long double, sd>));
    BOOST_CHECK((std::is_constructible_v<sd, float>));
    BOOST_CHECK((std::is_constructible_v<sd, double>));
    BOOST_CHECK((not std::is_constructible_v<sd, long double>));

    // Can be explicitly constructed from float, double and long double
    using sld = sf_test_cast<long double, policy::cast_from_primitive::less_precise_explicit>;

    BOOST_CHECK((not std::is_convertible_v<float, sld>));
    BOOST_CHECK((not std::is_convertible_v<double, sld>));
    BOOST_CHECK((not std::is_convertible_v<long double, sld>));
    BOOST_CHECK((std::is_constructible_v<sld, float>));
    BOOST_CHECK((std::is_constructible_v<sld, double>));
    BOOST_CHECK((std::is_constructible_v<sld, long double>));
}

BOOST_AUTO_TEST_CASE(safe_float_cast_from_primitive_all) {
    // Can be converted from float, double and long double
    using sf = sf_test_cast<float, policy::cast_from_primitive::all>;

    BOOST_CHECK((std::is_convertible_v<float, sf>));
    BOOST_CHECK((std::is_convertible_v<double, sf>));
    BOOST_CHECK((std::is_convertible_v<long double, sf>));
    BOOST_CHECK((std::is_constructible_v<sf, float>));
    BOOST_CHECK((std::is_constructible_v<sf, double>));
    BOOST_CHECK((std::is_constructible_v<sf, long double>));

    // Can be converted from float, double and long double
    using sd = sf_test_cast<double, policy::cast_from_primitive::all>;

    BOOST_CHECK((std::is_convertible_v<float, sd>));
    BOOST_CHECK((std::is_convertible_v<double, sd>));
    BOOST_CHECK((std::is_convertible_v<long double, sd>));
    BOOST_CHECK((std::is_constructible_v<sd, float>));
    BOOST_CHECK((std::is_constructible_v<sd, double>));
    BOOST_CHECK((std::is_constructible_v<sd, long double>));

    // Can be converted from float, double and long double
    using sld = sf_test_cast<long double, policy::cast_from_primitive::all>;

    BOOST_CHECK((std::is_convertible_v<float, sld>));
    BOOST_CHECK((std::is_convertible_v<double, sld>));
    BOOST_CHECK((std::is_convertible_v<long double, sld>));
    BOOST_CHECK((std::is_constructible_v<sld, float>));
    BOOST_CHECK((std::is_constructible_v<sld, double>));
    BOOST_CHECK((std::is_constructible_v<sld, long double>));
}

BOOST_AUTO_TEST_CASE(safe_float_cast_from_primitive_all_explicit) {
    // Can be explicitly constructed from float, double and long double
    using sf = sf_test_cast<float, policy::cast_from_primitive::all_explicit>;

    BOOST_CHECK((not std::is_convertible_v<float, sf>));
    BOOST_CHECK((not std::is_convertible_v<double, sf>));
    BOOST_CHECK((not std::is_convertible_v<long double, sf>));
    BOOST_CHECK((std::is_constructible_v<sf, float>));
    BOOST_CHECK((std::is_constructible_v<sf, double>));
    BOOST_CHECK((std::is_constructible_v<sf, long double>));

    // Can be explicitly constructed from float, double and long double
    using sd = sf_test_cast<double, policy::cast_from_primitive::all_explicit>;

    BOOST_CHECK((not std::is_convertible_v<float, sd>));
    BOOST_CHECK((not std::is_convertible_v<double, sd>));
    BOOST_CHECK((not std::is_convertible_v<long double, sd>));
    BOOST_CHECK((std::is_constructible_v<sd, float>));
    BOOST_CHECK((std::is_constructible_v<sd, double>));
    BOOST_CHECK((std::is_constructible_v<sd, long double>));

    // Can be explicitly constructed from float, double and long double
    using sld = sf_test_cast<long double, policy::cast_from_primitive::all_explicit>;

    BOOST_CHECK((not std::is_convertible_v<float, sld>));
    BOOST_CHECK((not std::is_convertible_v<double, sld>));
    BOOST_CHECK((not std::is_convertible_v<long double, sld>));
    BOOST_CHECK((std::is_constructible_v<sld, float>));
    BOOST_CHECK((std::is_constructible_v<sld, double>));
    BOOST_CHECK((std::is_constructible_v<sld, long double>));
}

BOOST_AUTO_TEST_CASE(safe_float_cast_to_primitive_none) {
    // Cannot be converted to any primitive
    using sf = sf_test_cast<float, policy::cast_to_primitive::none>;

    BOOST_CHECK((not std::is_convertible_v<sf, float>));
    BOOST_CHECK((not std::is_convertible_v<sf, double>));
    BOOST_CHECK((not std::is_convertible_v<sf, long double>));
    BOOST_CHECK((not std::is_constructible_v<float, sf>));
    BOOST_CHECK((not std::is_constructible_v<double, sf>));
    BOOST_CHECK((not std::is_constructible_v<long double, sf>));

    // Cannot be converted to any primitive
    using sd = sf_test_cast<double, policy::cast_to_primitive::none>;

    BOOST_CHECK((not std::is_convertible_v<sd, float>));
    BOOST_CHECK((not std::is_convertible_v<sd, double>));
    BOOST_CHECK((not std::is_convertible_v<sd, long double>));
    BOOST_CHECK((not std::is_constructible_v<float, sd>));
    BOOST_CHECK((not std::is_constructible_v<double, sd>));
    BOOST_CHECK((not std::is_constructible_v<long double, sd>));

    // Cannot be converted to any primitive
    using sld = sf_test_cast<long double, policy::cast_to_primitive::none>;

    BOOST_CHECK((not std::is_convertible_v<sld, float>));
    BOOST_CHECK((not std::is_convertible_v<sld, double>));
    BOOST_CHECK((not std::is_convertible_v<sld, long double>));
    BOOST_CHECK((not std::is_constructible_v<float, sld>));
    BOOST_CHECK((not std::is_constructible_v<double, sld>));
    BOOST_CHECK((not std::is_constructible_v<long double, sld>));
}

BOOST_AUTO_TEST_CASE(safe_float_cast_to_primitive_none_explicit) {
    // Cannot be converted to any primitive
    using sf = sf_test_cast<float, policy::cast_to_primitive::none_explicit>;

    BOOST_CHECK((not std::is_convertible_v<sf, float>));
    BOOST_CHECK((not std::is_convertible_v<sf, double>));
    BOOST_CHECK((not std::is_convertible_v<sf, long double>));
    BOOST_CHECK((not std::is_constructible_v<float, sf>));
    BOOST_CHECK((not std::is_constructible_v<double, sf>));
    BOOST_CHECK((not std::is_constructible_v<long double, sf>));

    // Cannot be converted to any primitive
    using sd = sf_test_cast<double, policy::cast_to_primitive::none_explicit>;

    BOOST_CHECK((not std::is_convertible_v<sd, float>));
    BOOST_CHECK((not std::is_convertible_v<sd, double>));
    BOOST_CHECK((not std::is_convertible_v<sd, long double>));
    BOOST_CHECK((not std::is_constructible_v<float, sd>));
    BOOST_CHECK((not std::is_constructible_v<double, sd>));
    BOOST_CHECK((not std::is_constructible_v<long double, sd>));

    // Cannot be converted to any primitive
    using sld = sf_test_cast<long double, policy::cast_to_primitive::none_explicit>;

    BOOST_CHECK((not std::is_convertible_v<sld, float>));
    BOOST_CHECK((not std::is_convertible_v<sld, double>));
    BOOST_CHECK((not std::is_convertible_v<sld, long double>));
    BOOST_CHECK((not std::is_constructible_v<float, sld>));
    BOOST_CHECK((not std::is_constructible_v<double, sld>));
    BOOST_CHECK((not std::is_constructible_v<long double, sld>));
}

BOOST_AUTO_TEST_CASE(safe_float_cast_to_primitive_same) {
    // Can be converted to float only
    using sf = sf_test_cast<float, policy::cast_to_primitive::same>;

    BOOST_CHECK((std::is_convertible_v<sf, float>));
    BOOST_CHECK((not std::is_convertible_v<sf, double>));
    BOOST_CHECK((not std::is_convertible_v<sf, long double>));
    BOOST_CHECK((std::is_constructible_v<float, sf>));
    BOOST_CHECK((not std::is_constructible_v<double, sf>));
    BOOST_CHECK((not std::is_constructible_v<long double, sf>));

    // Can be converted to double only
    using sd = sf_test_cast<double, policy::cast_to_primitive::same>;

    BOOST_CHECK((not std::is_convertible_v<sd, float>));
    BOOST_CHECK((std::is_convertible_v<sd, double>));
    BOOST_CHECK((not std::is_convertible_v<sd, long double>));
    BOOST_CHECK((not std::is_constructible_v<float, sd>));
    BOOST_CHECK((std::is_constructible_v<double, sd>));
    BOOST_CHECK((not std::is_constructible_v<long double, sd>));

    // Can be converted to long double only
    using sld = sf_test_cast<long double, policy::cast_to_primitive::same>;

    BOOST_CHECK((not std::is_convertible_v<sld, float>));
    BOOST_CHECK((not std::is_convertible_v<sld, double>));
    BOOST_CHECK((std::is_convertible_v<sld, long double>));
    BOOST_CHECK((not std::is_constructible_v<float, sld>));
    BOOST_CHECK((not std::is_constructible_v<double, sld>));
    BOOST_CHECK((std::is_constructible_v<long double, sld>));
}

BOOST_AUTO_TEST_CASE(safe_float_cast_to_primitive_same_explicit) {
    // Cannot be converted to any primitive but is explicitly castable to float
    using sf = sf_test_cast<float, policy::cast_to_primitive::same_explicit>;

    BOOST_CHECK((not std::is_convertible_v<sf, float>));
    BOOST_CHECK((not std::is_convertible_v<sf, double>));
    BOOST_CHECK((not std::is_convertible_v<sf, long double>));
    BOOST_CHECK((std::is_constructible_v<float, sf>));
    BOOST_CHECK((not std::is_constructible_v<double, sf>));
    BOOST_CHECK((not std::is_constructible_v<long double, sf>));

    // Cannot be converted to any primitive but is explicitly castable to double
    using sd = sf_test_cast<double, policy::cast_to_primitive::same_explicit>;

    BOOST_CHECK((not std::is_convertible_v<sd, float>));
    BOOST_CHECK((not std::is_convertible_v<sd, double>));
    BOOST_CHECK((not std::is_convertible_v<sd, long double>));
    BOOST_CHECK((not std::is_constructible_v<float, sd>));
    BOOST_CHECK((std::is_constructible_v<double, sd>));
    BOOST_CHECK((not std::is_constructible_v<long double, sd>));

    // Cannot be converted to any primitive but is explicitly castable to long double
    using sld = sf_test_cast<long double, policy::cast_to_primitive::same_explicit>;

    BOOST_CHECK((not std::is_convertible_v<sld, float>));
    BOOST_CHECK((not std::is_convertible_v<sld, double>));
    BOOST_CHECK((not std::is_convertible_v<sld, long double>));
    BOOST_CHECK((not std::is_constructible_v<float, sld>));
    BOOST_CHECK((not std::is_constructible_v<double, sld>));
    BOOST_CHECK((std::is_constructible_v<long double, sld>));
}

BOOST_AUTO_TEST_CASE(safe_float_cast_to_primitive_more_precise) {
    // Can be converted to float, double and long double
    using sf = sf_test_cast<float, policy::cast_to_primitive::more_precise>;

    BOOST_CHECK((std::is_convertible_v<sf, float>));
    BOOST_CHECK((std::is_convertible_v<sf, double>));
    BOOST_CHECK((std::is_convertible_v<sf, long double>));
    BOOST_CHECK((std::is_constructible_v<float, sf>));
    BOOST_CHECK((std::is_constructible_v<double, sf>));
    BOOST_CHECK((std::is_constructible_v<long double, sf>));

    // Can be converted to double and long double
    using sd = sf_test_cast<double, policy::cast_to_primitive::more_precise>;

    BOOST_CHECK((not std::is_convertible_v<sd, float>));
    BOOST_CHECK((std::is_convertible_v<sd, double>));
    BOOST_CHECK((std::is_convertible_v<sd, long double>));
    BOOST_CHECK((not std::is_constructible_v<float, sd>));
    BOOST_CHECK((std::is_constructible_v<double, sd>));
    BOOST_CHECK((std::is_constructible_v<long double, sd>));

    // Can be converted to long double only
    using sld = sf_test_cast<long double, policy::cast_to_primitive::more_precise>;

    BOOST_CHECK((not std::is_convertible_v<sld, float>));
    BOOST_CHECK((not std::is_convertible_v<sld, double>));
    BOOST_CHECK((std::is_convertible_v<sld, long double>));
    BOOST_CHECK((not std::is_constructible_v<float, sld>));
    BOOST_CHECK((not std::is_constructible_v<double, sld>));
    BOOST_CHECK((std::is_constructible_v<long double, sld>));
}

BOOST_AUTO_TEST_CASE(safe_float_cast_to_primitive_more_precise_explicit) {
    // Can be explicitly converted to float, double and long double
    using sf = sf_test_cast<float, policy::cast_to_primitive::more_precise_explicit>;

    BOOST_CHECK((not std::is_convertible_v<sf, float>));
    BOOST_CHECK((not std::is_convertible_v<sf, double>));
    BOOST_CHECK((not std::is_convertible_v<sf, long double>));
    BOOST_CHECK((std::is_constructible_v<float, sf>));
    BOOST_CHECK((std::is_constructible_v<double, sf>));
    BOOST_CHECK((std::is_constructible_v<long double, sf>));

    // Can be explicitly converted to double and long double
    using sd = sf_test_cast<double, policy::cast_to_primitive::more_precise_explicit>;

    BOOST_CHECK((not std::is_convertible_v<sd, float>));
    BOOST_CHECK((not std::is_convertible_v<sd, double>));
    BOOST_CHECK((not std::is_convertible_v<sd, long double>));
    BOOST_CHECK((not std::is_constructible_v<float, sd>));
    BOOST_CHECK((std::is_constructible_v<double, sd>));
    BOOST_CHECK((std::is_constructible_v<long double, sd>));

    // Can be explicitly converted to long double only
    using sld = sf_test_cast<long double, policy::cast_to_primitive::more_precise_explicit>;

    BOOST_CHECK((not std::is_convertible_v<sld, float>));
    BOOST_CHECK((not std::is_convertible_v<sld, double>));
    BOOST_CHECK((not std::is_convertible_v<sld, long double>));
    BOOST_CHECK((not std::is_constructible_v<float, sld>));
    BOOST_CHECK((not std::is_constructible_v<double, sld>));
    BOOST_CHECK((std::is_constructible_v<long double, sld>));
}

BOOST_AUTO_TEST_CASE(safe_float_cast_to_primitive_less_precise) {
    // Can be converted to float
    using sf = sf_test_cast<float, policy::cast_to_primitive::less_precise>;

    BOOST_CHECK((std::is_convertible_v<sf, float>));
    BOOST_CHECK((not std::is_convertible_v<sf, double>));
    BOOST_CHECK((not std::is_convertible_v<sf, long double>));
    BOOST_CHECK((std::is_constructible_v<float, sf>));
    BOOST_CHECK((not std::is_constructible_v<double, sf>));
    BOOST_CHECK((not std::is_constructible_v<long double, sf>));

    // Can be converted to float and double
    using sd = sf_test_cast<double, policy::cast_to_primitive::less_precise>;

    BOOST_CHECK((std::is_convertible_v<sd, float>));
    BOOST_CHECK((std::is_convertible_v<sd, double>));
    BOOST_CHECK((not std::is_convertible_v<sd, long double>));
    BOOST_CHECK((std::is_constructible_v<float, sd>));
    BOOST_CHECK((std::is_constructible_v<double, sd>));
    BOOST_CHECK((not std::is_constructible_v<long double, sd>));

    // Can be converted to float, double and long double
    using sld = sf_test_cast<long double, policy::cast_to_primitive::less_precise>;

    BOOST_CHECK((std::is_convertible_v<sld, float>));
    BOOST_CHECK((std::is_convertible_v<sld, double>));
    BOOST_CHECK((std::is_convertible_v<sld, long double>));
    BOOST_CHECK((std::is_constructible_v<float, sld>));
    BOOST_CHECK((std::is_constructible_v<double, sld>));
    BOOST_CHECK((std::is_constructible_v<long double, sld>));
}

BOOST_AUTO_TEST_CASE(safe_float_cast_to_primitive_less_precise_explicit) {
    // Can be explicitly converted to float
    using sf = sf_test_cast<float, policy::cast_to_primitive::less_precise_explicit>;

    BOOST_CHECK((not std::is_convertible_v<sf, float>));
    BOOST_CHECK((not std::is_convertible_v<sf, double>));
    BOOST_CHECK((not std::is_convertible_v<sf, long double>));
    BOOST_CHECK((std::is_constructible_v<float, sf>));
    BOOST_CHECK((not std::is_constructible_v<double, sf>));
    BOOST_CHECK((not std::is_constructible_v<long double, sf>));

    // Can be explicitly converted to float and double
    using sd = sf_test_cast<double, policy::cast_to_primitive::less_precise_explicit>;

    BOOST_CHECK((not std::is_convertible_v<sd, float>));
    BOOST_CHECK((not std::is_convertible_v<sd, double>));
    BOOST_CHECK((not std::is_convertible_v<sd, long double>));
    BOOST_CHECK((std::is_constructible_v<float, sd>));
    BOOST_CHECK((std::is_constructible_v<double, sd>));
    BOOST_CHECK((not std::is_constructible_v<long double, sd>));

    // Can be explicitly converted to float, double and long double
    using sld = sf_test_cast<long double, policy::cast_to_primitive::less_precise_explicit>;

    BOOST_CHECK((not std::is_convertible_v<sld, float>));
    BOOST_CHECK((not std::is_convertible_v<sld, double>));
    BOOST_CHECK((not std::is_convertible_v<sld, long double>));
    BOOST_CHECK((std::is_constructible_v<float, sld>));
    BOOST_CHECK((std::is_constructible_v<double, sld>));
    BOOST_CHECK((std::is_constructible_v<long double, sld>));
}

BOOST_AUTO_TEST_CASE(safe_float_cast_to_primitive_all) {
    // Can be converted to float, double and long double
    using sf = sf_test_cast<float, policy::cast_to_primitive::all>;

    BOOST_CHECK((std::is_convertible_v<sf, float>));
    BOOST_CHECK((std::is_convertible_v<sf, double>));
    BOOST_CHECK((std::is_convertible_v<sf, long double>));
    BOOST_CHECK((std::is_constructible_v<float, sf>));
    BOOST_CHECK((std::is_constructible_v<double, sf>));
    BOOST_CHECK((std::is_constructible_v<long double, sf>));

    // Can be converted to float, double and long double
    using sd = sf_test_cast<double, policy::cast_to_primitive::all>;

    BOOST_CHECK((std::is_convertible_v<sd, float>));
    BOOST_CHECK((std::is_convertible_v<sd, double>));
    BOOST_CHECK((std::is_convertible_v<sd, long double>));
    BOOST_CHECK((std::is_constructible_v<float, sd>));
    BOOST_CHECK((std::is_constructible_v<double, sd>));
    BOOST_CHECK((std::is_constructible_v<long double, sd>));

    // Can be converted to float, double and long double
    using sld = sf_test_cast<long double, policy::cast_to_primitive::all>;

    BOOST_CHECK((std::is_convertible_v<sld, float>));
    BOOST_CHECK((std::is_convertible_v<sld, double>));
    BOOST_CHECK((std::is_convertible_v<sld, long double>));
    BOOST_CHECK((std::is_constructible_v<float, sld>));
    BOOST_CHECK((std::is_constructible_v<double, sld>));
    BOOST_CHECK((std::is_constructible_v<long double, sld>));
}

BOOST_AUTO_TEST_CASE(safe_float_cast_to_primitive_all_explicit) {
    // Can be explicitly converted to float, double and long double
    using sf = sf_test_cast<float, policy::cast_to_primitive::all_explicit>;

    BOOST_CHECK((not std::is_convertible_v<sf, float>));
    BOOST_CHECK((not std::is_convertible_v<sf, double>));
    BOOST_CHECK((not std::is_convertible_v<sf, long double>));
    BOOST_CHECK((std::is_constructible_v<float, sf>));
    BOOST_CHECK((std::is_constructible_v<double, sf>));
    BOOST_CHECK((std::is_constructible_v<long double, sf>));

    // Can be explicitly converted to float, double and long double
    using sd = sf_test_cast<double, policy::cast_to_primitive::all_explicit>;

    BOOST_CHECK((not std::is_convertible_v<sd, float>));
    BOOST_CHECK((not std::is_convertible_v<sd, double>));
    BOOST_CHECK((not std::is_convertible_v<sd, long double>));
    BOOST_CHECK((std::is_constructible_v<float, sd>));
    BOOST_CHECK((std::is_constructible_v<double, sd>));
    BOOST_CHECK((std::is_constructible_v<long double, sd>));

    // Can be explicitly converted to float, double and long double
    using sld = sf_test_cast<long double, policy::cast_to_primitive::all_explicit>;

    BOOST_CHECK((not std::is_convertible_v<sld, float>));
    BOOST_CHECK((not std::is_convertible_v<sld, double>));
    BOOST_CHECK((not std::is_convertible_v<sld, long double>));
    BOOST_CHECK((std::is_constructible_v<float, sld>));
    BOOST_CHECK((std::is_constructible_v<double, sld>));
    BOOST_CHECK((std::is_constructible_v<long double, sld>));
}

BOOST_AUTO_TEST_CASE_TEMPLATE( safe_float_cast_from_int, FPT, test_types){
    //set initial values
    int a = 0;
    int b = 1;
    int c = -284554;
    int d = 684654;
    //create safe version of the values
    using sf = safe_float<FPT, policy::check_all, policy::on_fail_throw, policy::allow_cast_from<int>::policy>;
    sf sa = a;
    sf sb = b;
    sf sc = c;
    sf sd = d;
    //check the values match when obtaining the stored value
    BOOST_CHECK_EQUAL(a, sa.get_stored_value());
    BOOST_CHECK_EQUAL(b, sb.get_stored_value());
    BOOST_CHECK_EQUAL(c, sc.get_stored_value());
    BOOST_CHECK_EQUAL(d, sd.get_stored_value());
}

BOOST_AUTO_TEST_CASE_TEMPLATE( safe_float_explicit_cast_from_int, FPT, test_types){
    //set initial values
    int a = 0;
    int b = 1;
    int c = -284554;
    int d = 684654;
    //create safe version of the values
    using sf = safe_float<FPT, policy::check_all, policy::on_fail_throw, policy::allow_explicit_cast_from<int>::policy>;
    sf sa(a);
    sf sb(b);
    sf sc(c);
    sf sd(d);
    //check the values match when obtaining the stored value
    BOOST_CHECK_EQUAL(a, sa.get_stored_value());
    BOOST_CHECK_EQUAL(b, sb.get_stored_value());
    BOOST_CHECK_EQUAL(c, sc.get_stored_value());
    BOOST_CHECK_EQUAL(d, sd.get_stored_value());
}

BOOST_AUTO_TEST_CASE_TEMPLATE( safe_float_cast_to_int, FPT, test_types){
    //create safe version of the values
    using sf = safe_float<FPT, policy::check_all, policy::on_fail_throw, policy::allow_cast_to<int>::policy>;
    sf sa((FPT)0);
    sf sb((FPT)1);
    sf sc((FPT)-284554);
    sf sd((FPT)684654);

    // cast to int
    int a = sa;
    int b = sb;
    int c = sc;
    int d = sd;

    //check the values match when obtaining the stored value
    BOOST_CHECK_EQUAL(a, sa.get_stored_value());
    BOOST_CHECK_EQUAL(b, sb.get_stored_value());
    BOOST_CHECK_EQUAL(c, sc.get_stored_value());
    BOOST_CHECK_EQUAL(d, sd.get_stored_value());
}

BOOST_AUTO_TEST_CASE_TEMPLATE( safe_float_explicit_cast_to_int, FPT, test_types){
    //create safe version of the values
    using sf = safe_float<FPT, policy::check_all, policy::on_fail_throw, policy::allow_cast_to<int>::policy>;
    sf sa((FPT)0);
    sf sb((FPT)1);
    sf sc((FPT)-284554);
    sf sd((FPT)684654);

    // cast to int
    int a(sa);
    int b(sb);
    int c(sc);
    int d(sd);

    //check the values match when obtaining the stored value
    BOOST_CHECK_EQUAL(a, sa.get_stored_value());
    BOOST_CHECK_EQUAL(b, sb.get_stored_value());
    BOOST_CHECK_EQUAL(c, sc.get_stored_value());
    BOOST_CHECK_EQUAL(d, sd.get_stored_value());
}

BOOST_AUTO_TEST_CASE_TEMPLATE( safe_float_explicit_cast_from_safe, FPT, test_types){
    // base values
    using base_sf = safe_float<FPT, policy::check_bothflow>;
    base_sf a((FPT)0.0);
    base_sf b((FPT)1.0);

    //create safe version of the values
    using sf = safe_float<FPT, policy::check_underflow, policy::on_fail_throw, policy::cast_from_safe_policy::more_restrictive>;

    sf sa = a;
    sf sb = b;

    BOOST_CHECK((std::is_convertible_v<base_sf, sf>));
    BOOST_CHECK((std::is_constructible_v<sf, base_sf>));

    //check the values match when obtaining the stored value
    BOOST_CHECK_EQUAL(a.get_stored_value(), sa.get_stored_value());
    BOOST_CHECK_EQUAL(b.get_stored_value(), sb.get_stored_value());
}

template<typename FP, template<typename> typename CAST>
using sf_all = safe_float<FP, policy::check_all, policy::on_fail_throw, CAST>;

template<typename FP, template<typename> typename CAST>
using sf_of = safe_float<FP, policy::check_overflow, policy::on_fail_throw, CAST>;

template<typename FP, template<typename> typename CAST>
using sf_add1 = safe_float<FP, policy::compose_check<policy::check_addition_overflow, policy::check_addition_invalid_result>::policy, policy::on_fail_throw, CAST>;

template<typename FP, template<typename> typename CAST>
using sf_add2 = safe_float<FP, policy::compose_check<policy::check_addition_invalid_result, policy::check_addition_overflow>::policy, policy::on_fail_throw, CAST>;

BOOST_AUTO_TEST_CASE_TEMPLATE(safe_float_cast_from_safe_more_restrictive, FPT, test_types) {
    using source_all = sf_all<FPT, policy::cast_none>;
    using source_of = sf_of<FPT, policy::cast_none>;
    using source_add1 = sf_add1<FPT, policy::cast_none>;
    using source_add2 = sf_add2<FPT, policy::cast_none>;

    using target_all = sf_all<FPT, policy::cast_from_safe_policy::more_restrictive>;
    using target_of = sf_of<FPT, policy::cast_from_safe_policy::more_restrictive>;
    using target_add1 = sf_add1<FPT, policy::cast_from_safe_policy::more_restrictive>;
    using target_add2 = sf_add2<FPT, policy::cast_from_safe_policy::more_restrictive>;

    BOOST_CHECK((std::is_convertible_v<source_all, target_all>));
    BOOST_CHECK((std::is_convertible_v<source_all, target_of>));
    BOOST_CHECK((std::is_convertible_v<source_all, target_add1>));
    BOOST_CHECK((std::is_convertible_v<source_all, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_all>));

    BOOST_CHECK((not std::is_convertible_v<source_of, target_all>));
    BOOST_CHECK((std::is_convertible_v<source_of, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add1, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add2, source_of>));

    BOOST_CHECK((not std::is_convertible_v<source_add1, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_of>));
    BOOST_CHECK((std::is_convertible_v<source_add1, target_add1>));
    BOOST_CHECK((std::is_convertible_v<source_add1, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_add1>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add1>));

    BOOST_CHECK((not std::is_convertible_v<source_add2, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_of>));
    BOOST_CHECK((std::is_convertible_v<source_add2, target_add1>));
    BOOST_CHECK((std::is_convertible_v<source_add2, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add2>));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(safe_float_cast_from_safe_more_restrictive_explicit, FPT, test_types) {
    using source_all = sf_all<FPT, policy::cast_none>;
    using source_of = sf_of<FPT, policy::cast_none>;
    using source_add1 = sf_add1<FPT, policy::cast_none>;
    using source_add2 = sf_add2<FPT, policy::cast_none>;

    using target_all = sf_all<FPT, policy::cast_from_safe_policy::more_restrictive_explicit>;
    using target_of = sf_of<FPT, policy::cast_from_safe_policy::more_restrictive_explicit>;
    using target_add1 = sf_add1<FPT, policy::cast_from_safe_policy::more_restrictive_explicit>;
    using target_add2 = sf_add2<FPT, policy::cast_from_safe_policy::more_restrictive_explicit>;

    BOOST_CHECK((not std::is_convertible_v<source_all, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_all>));

    BOOST_CHECK((not std::is_convertible_v<source_of, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add1, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add2, source_of>));

    BOOST_CHECK((not std::is_convertible_v<source_add1, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_add1>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add1>));

    BOOST_CHECK((not std::is_convertible_v<source_add2, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add2>));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(safe_float_cast_from_safe_less_restrictive, FPT, test_types) {
    using source_all = sf_all<FPT, policy::cast_none>;
    using source_of = sf_of<FPT, policy::cast_none>;
    using source_add1 = sf_add1<FPT, policy::cast_none>;
    using source_add2 = sf_add2<FPT, policy::cast_none>;

    using target_all = sf_all<FPT, policy::cast_from_safe_policy::less_restrictive>;
    using target_of = sf_of<FPT, policy::cast_from_safe_policy::less_restrictive>;
    using target_add1 = sf_add1<FPT, policy::cast_from_safe_policy::less_restrictive>;
    using target_add2 = sf_add2<FPT, policy::cast_from_safe_policy::less_restrictive>;

    BOOST_CHECK((std::is_convertible_v<source_all, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_add1, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_add2, source_all>));

    BOOST_CHECK((std::is_convertible_v<source_of, target_all>));
    BOOST_CHECK((std::is_convertible_v<source_of, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add1, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add2, source_of>));

    BOOST_CHECK((std::is_convertible_v<source_add1, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_of>));
    BOOST_CHECK((std::is_convertible_v<source_add1, target_add1>));
    BOOST_CHECK((std::is_convertible_v<source_add1, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_add1>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add1>));

    BOOST_CHECK((std::is_convertible_v<source_add2, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_of>));
    BOOST_CHECK((std::is_convertible_v<source_add2, target_add1>));
    BOOST_CHECK((std::is_convertible_v<source_add2, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add2>));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(safe_float_cast_from_safe_less_restrictive_explicit, FPT, test_types) {
    using source_all = sf_all<FPT, policy::cast_none>;
    using source_of = sf_of<FPT, policy::cast_none>;
    using source_add1 = sf_add1<FPT, policy::cast_none>;
    using source_add2 = sf_add2<FPT, policy::cast_none>;

    using target_all = sf_all<FPT, policy::cast_from_safe_policy::less_restrictive_explicit>;
    using target_of = sf_of<FPT, policy::cast_from_safe_policy::less_restrictive_explicit>;
    using target_add1 = sf_add1<FPT, policy::cast_from_safe_policy::less_restrictive_explicit>;
    using target_add2 = sf_add2<FPT, policy::cast_from_safe_policy::less_restrictive_explicit>;

    BOOST_CHECK((not std::is_convertible_v<source_all, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_add1, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_add2, source_all>));

    BOOST_CHECK((not std::is_convertible_v<source_of, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add1, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add2, source_of>));

    BOOST_CHECK((not std::is_convertible_v<source_add1, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_add1>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add1>));

    BOOST_CHECK((not std::is_convertible_v<source_add2, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add2>));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(safe_float_cast_from_safe_equivalent, FPT, test_types) {
    using source_all = sf_all<FPT, policy::cast_none>;
    using source_of = sf_of<FPT, policy::cast_none>;
    using source_add1 = sf_add1<FPT, policy::cast_none>;
    using source_add2 = sf_add2<FPT, policy::cast_none>;

    using target_all = sf_all<FPT, policy::cast_from_safe_policy::equivalent>;
    using target_of = sf_of<FPT, policy::cast_from_safe_policy::equivalent>;
    using target_add1 = sf_add1<FPT, policy::cast_from_safe_policy::equivalent>;
    using target_add2 = sf_add2<FPT, policy::cast_from_safe_policy::equivalent>;

    BOOST_CHECK((std::is_convertible_v<source_all, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_add1, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_add2, source_all>));

    BOOST_CHECK((not std::is_convertible_v<source_of, target_all>));
    BOOST_CHECK((std::is_convertible_v<source_of, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add1, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add2, source_of>));

    BOOST_CHECK((not std::is_convertible_v<source_add1, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_of>));
    BOOST_CHECK((std::is_convertible_v<source_add1, target_add1>));
    BOOST_CHECK((std::is_convertible_v<source_add1, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_add1>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add1>));

    BOOST_CHECK((not std::is_convertible_v<source_add2, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_of>));
    BOOST_CHECK((std::is_convertible_v<source_add2, target_add1>));
    BOOST_CHECK((std::is_convertible_v<source_add2, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add2>));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(safe_float_cast_from_safe_equivalent_explicit, FPT, test_types) {
    using source_all = sf_all<FPT, policy::cast_none>;
    using source_of = sf_of<FPT, policy::cast_none>;
    using source_add1 = sf_add1<FPT, policy::cast_none>;
    using source_add2 = sf_add2<FPT, policy::cast_none>;

    using target_all = sf_all<FPT, policy::cast_from_safe_policy::equivalent_explicit>;
    using target_of = sf_of<FPT, policy::cast_from_safe_policy::equivalent_explicit>;
    using target_add1 = sf_add1<FPT, policy::cast_from_safe_policy::equivalent_explicit>;
    using target_add2 = sf_add2<FPT, policy::cast_from_safe_policy::equivalent_explicit>;

    BOOST_CHECK((not std::is_convertible_v<source_all, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_add1, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_add2, source_all>));

    BOOST_CHECK((not std::is_convertible_v<source_of, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add1, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add2, source_of>));

    BOOST_CHECK((not std::is_convertible_v<source_add1, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_add1>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add1>));

    BOOST_CHECK((not std::is_convertible_v<source_add2, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add2>));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(safe_float_cast_from_safe_all, FPT, test_types) {
    using source_all = sf_all<FPT, policy::cast_none>;
    using source_of = sf_of<FPT, policy::cast_none>;
    using source_add1 = sf_add1<FPT, policy::cast_none>;
    using source_add2 = sf_add2<FPT, policy::cast_none>;

    using target_all = sf_all<FPT, policy::cast_from_safe_policy::all>;
    using target_of = sf_of<FPT, policy::cast_from_safe_policy::all>;
    using target_add1 = sf_add1<FPT, policy::cast_from_safe_policy::all>;
    using target_add2 = sf_add2<FPT, policy::cast_from_safe_policy::all>;

    BOOST_CHECK((std::is_convertible_v<source_all, target_all>));
    BOOST_CHECK((std::is_convertible_v<source_all, target_of>));
    BOOST_CHECK((std::is_convertible_v<source_all, target_add1>));
    BOOST_CHECK((std::is_convertible_v<source_all, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_all>));

    BOOST_CHECK((std::is_convertible_v<source_of, target_all>));
    BOOST_CHECK((std::is_convertible_v<source_of, target_of>));
    BOOST_CHECK((std::is_convertible_v<source_of, target_add1>));
    BOOST_CHECK((std::is_convertible_v<source_of, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_of>));

    BOOST_CHECK((std::is_convertible_v<source_add1, target_all>));
    BOOST_CHECK((std::is_convertible_v<source_add1, target_of>));
    BOOST_CHECK((std::is_convertible_v<source_add1, target_add1>));
    BOOST_CHECK((std::is_convertible_v<source_add1, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add1>));

    BOOST_CHECK((std::is_convertible_v<source_add2, target_all>));
    BOOST_CHECK((std::is_convertible_v<source_add2, target_of>));
    BOOST_CHECK((std::is_convertible_v<source_add2, target_add1>));
    BOOST_CHECK((std::is_convertible_v<source_add2, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add2>));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(safe_float_cast_from_safe_all_explicit, FPT, test_types) {
    using source_all = sf_all<FPT, policy::cast_none>;
    using source_of = sf_of<FPT, policy::cast_none>;
    using source_add1 = sf_add1<FPT, policy::cast_none>;
    using source_add2 = sf_add2<FPT, policy::cast_none>;

    using target_all = sf_all<FPT, policy::cast_from_safe_policy::all_explicit>;
    using target_of = sf_of<FPT, policy::cast_from_safe_policy::all_explicit>;
    using target_add1 = sf_add1<FPT, policy::cast_from_safe_policy::all_explicit>;
    using target_add2 = sf_add2<FPT, policy::cast_from_safe_policy::all_explicit>;

    BOOST_CHECK((not std::is_convertible_v<source_all, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_all>));

    BOOST_CHECK((not std::is_convertible_v<source_of, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_of>));

    BOOST_CHECK((not std::is_convertible_v<source_add1, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add1>));

    BOOST_CHECK((not std::is_convertible_v<source_add2, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add2>));
}

BOOST_AUTO_TEST_CASE(safe_float_cast_from_safe) {
    using source_float_all = sf_all<float, policy::cast_none>;
    using source_double_of = sf_of<double, policy::cast_none>;
    using source_long_double_add1 = sf_add1<long double, policy::cast_none>;
    using source_float_add2 = sf_add2<float, policy::cast_none>;

    // Test of some combination of criteria on floating point types and policies
    using target_float_all = sf_all<float, policy::cast_from_safe<policy::cast_from_primitive::same, policy::cast_from_safe_policy::less_restrictive>::policy>;
    using target_double_of = sf_of<double, policy::cast_from_safe<policy::cast_from_primitive::less_precise, policy::cast_from_safe_policy::more_restrictive>::policy>;
    using target_double_add1 = sf_add1<double, policy::cast_from_safe<policy::cast_from_primitive::all, policy::cast_from_safe_policy::all>::policy>;
    using target_long_double_add2 = sf_add2<long double, policy::cast_from_safe<policy::cast_from_primitive::more_precise, policy::cast_from_safe_policy::equivalent>::policy>;

    BOOST_CHECK((std::is_convertible_v<source_float_all, target_float_all>));
    BOOST_CHECK((std::is_convertible_v<source_float_all, target_double_of>));
    BOOST_CHECK((std::is_convertible_v<source_float_all, target_double_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_float_all, target_long_double_add2>));
    BOOST_CHECK((std::is_constructible_v<target_float_all, source_float_all>));
    BOOST_CHECK((std::is_constructible_v<target_double_of, source_float_all>));
    BOOST_CHECK((std::is_constructible_v<target_double_add1, source_float_all>));
    BOOST_CHECK((not std::is_constructible_v<target_long_double_add2, source_float_all>));

    BOOST_CHECK((not std::is_convertible_v<source_double_of, target_float_all>));
    BOOST_CHECK((std::is_convertible_v<source_double_of, target_double_of>));
    BOOST_CHECK((std::is_convertible_v<source_double_of, target_double_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_double_of, target_long_double_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_float_all, source_double_of>));
    BOOST_CHECK((std::is_constructible_v<target_double_of, source_double_of>));
    BOOST_CHECK((std::is_constructible_v<target_double_add1, source_double_of>));
    BOOST_CHECK((not std::is_constructible_v<target_long_double_add2, source_double_of>));

    BOOST_CHECK((not std::is_convertible_v<source_long_double_add1, target_float_all>));
    BOOST_CHECK((not std::is_convertible_v<source_long_double_add1, target_double_of>));
    BOOST_CHECK((std::is_convertible_v<source_long_double_add1, target_double_add1>));
    BOOST_CHECK((std::is_convertible_v<source_long_double_add1, target_long_double_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_float_all, source_long_double_add1>));
    BOOST_CHECK((not std::is_constructible_v<target_double_of, source_long_double_add1>));
    BOOST_CHECK((std::is_constructible_v<target_double_add1, source_long_double_add1>));
    BOOST_CHECK((std::is_constructible_v<target_long_double_add2, source_long_double_add1>));

    BOOST_CHECK((std::is_convertible_v<source_float_add2, target_float_all>));
    BOOST_CHECK((not std::is_convertible_v<source_float_add2, target_double_of>));
    BOOST_CHECK((std::is_convertible_v<source_float_add2, target_double_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_float_add2, target_long_double_add2>));
    BOOST_CHECK((std::is_constructible_v<target_float_all, source_float_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_double_of, source_float_add2>));
    BOOST_CHECK((std::is_constructible_v<target_double_add1, source_float_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_long_double_add2, source_float_add2>));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(safe_float_cast_to_safe_more_restrictive, FPT, test_types) {
    using source_all = sf_all<FPT, policy::cast_to_safe_policy::more_restrictive>;
    using source_of = sf_of<FPT, policy::cast_to_safe_policy::more_restrictive>;
    using source_add1 = sf_add1<FPT, policy::cast_to_safe_policy::more_restrictive>;
    using source_add2 = sf_add2<FPT, policy::cast_to_safe_policy::more_restrictive>;

    using target_all = sf_all<FPT, policy::cast_none>;
    using target_of = sf_of<FPT, policy::cast_none>;
    using target_add1 = sf_add1<FPT, policy::cast_none>;
    using target_add2 = sf_add2<FPT, policy::cast_none>;

    BOOST_CHECK((std::is_convertible_v<source_all, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_add1, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_add2, source_all>));

    BOOST_CHECK((std::is_convertible_v<source_of, target_all>));
    BOOST_CHECK((std::is_convertible_v<source_of, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add1, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add2, source_of>));

    BOOST_CHECK((std::is_convertible_v<source_add1, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_of>));
    BOOST_CHECK((std::is_convertible_v<source_add1, target_add1>));
    BOOST_CHECK((std::is_convertible_v<source_add1, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_add1>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add1>));

    BOOST_CHECK((std::is_convertible_v<source_add2, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_of>));
    BOOST_CHECK((std::is_convertible_v<source_add2, target_add1>));
    BOOST_CHECK((std::is_convertible_v<source_add2, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add2>));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(safe_float_cast_to_safe_more_restrictive_explicit, FPT, test_types) {
    using source_all = sf_all<FPT, policy::cast_to_safe_policy::more_restrictive_explicit>;
    using source_of = sf_of<FPT, policy::cast_to_safe_policy::more_restrictive_explicit>;
    using source_add1 = sf_add1<FPT, policy::cast_to_safe_policy::more_restrictive_explicit>;
    using source_add2 = sf_add2<FPT, policy::cast_to_safe_policy::more_restrictive_explicit>;

    using target_all = sf_all<FPT, policy::cast_none>;
    using target_of = sf_of<FPT, policy::cast_none>;
    using target_add1 = sf_add1<FPT, policy::cast_none>;
    using target_add2 = sf_add2<FPT, policy::cast_none>;

    BOOST_CHECK((not std::is_convertible_v<source_all, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_add1, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_add2, source_all>));

    BOOST_CHECK((not std::is_convertible_v<source_of, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add1, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add2, source_of>));

    BOOST_CHECK((not std::is_convertible_v<source_add1, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_add1>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add1>));

    BOOST_CHECK((not std::is_convertible_v<source_add2, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add2>));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(safe_float_cast_to_safe_less_restrictive, FPT, test_types) {
    using source_all = sf_all<FPT, policy::cast_to_safe_policy::less_restrictive>;
    using source_of = sf_of<FPT, policy::cast_to_safe_policy::less_restrictive>;
    using source_add1 = sf_add1<FPT, policy::cast_to_safe_policy::less_restrictive>;
    using source_add2 = sf_add2<FPT, policy::cast_to_safe_policy::less_restrictive>;

    using target_all = sf_all<FPT, policy::cast_none>;
    using target_of = sf_of<FPT, policy::cast_none>;
    using target_add1 = sf_add1<FPT, policy::cast_none>;
    using target_add2 = sf_add2<FPT, policy::cast_none>;

    BOOST_CHECK((std::is_convertible_v<source_all, target_all>));
    BOOST_CHECK((std::is_convertible_v<source_all, target_of>));
    BOOST_CHECK((std::is_convertible_v<source_all, target_add1>));
    BOOST_CHECK((std::is_convertible_v<source_all, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_all>));

    BOOST_CHECK((not std::is_convertible_v<source_of, target_all>));
    BOOST_CHECK((std::is_convertible_v<source_of, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add1, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add2, source_of>));

    BOOST_CHECK((not std::is_convertible_v<source_add1, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_of>));
    BOOST_CHECK((std::is_convertible_v<source_add1, target_add1>));
    BOOST_CHECK((std::is_convertible_v<source_add1, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_add1>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add1>));

    BOOST_CHECK((not std::is_convertible_v<source_add2, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_of>));
    BOOST_CHECK((std::is_convertible_v<source_add2, target_add1>));
    BOOST_CHECK((std::is_convertible_v<source_add2, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add2>));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(safe_float_cast_to_safe_less_restrictive_explicit, FPT, test_types) {
    using source_all = sf_all<FPT, policy::cast_to_safe_policy::less_restrictive_explicit>;
    using source_of = sf_of<FPT, policy::cast_to_safe_policy::less_restrictive_explicit>;
    using source_add1 = sf_add1<FPT, policy::cast_to_safe_policy::less_restrictive_explicit>;
    using source_add2 = sf_add2<FPT, policy::cast_to_safe_policy::less_restrictive_explicit>;

    using target_all = sf_all<FPT, policy::cast_none>;
    using target_of = sf_of<FPT, policy::cast_none>;
    using target_add1 = sf_add1<FPT, policy::cast_none>;
    using target_add2 = sf_add2<FPT, policy::cast_none>;

    BOOST_CHECK((not std::is_convertible_v<source_all, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_all>));

    BOOST_CHECK((not std::is_convertible_v<source_of, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add1, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add2, source_of>));

    BOOST_CHECK((not std::is_convertible_v<source_add1, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_add1>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add1>));

    BOOST_CHECK((not std::is_convertible_v<source_add2, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add2>));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(safe_float_cast_to_safe_equivalent, FPT, test_types) {
    using source_all = sf_all<FPT, policy::cast_to_safe_policy::equivalent>;
    using source_of = sf_of<FPT, policy::cast_to_safe_policy::equivalent>;
    using source_add1 = sf_add1<FPT, policy::cast_to_safe_policy::equivalent>;
    using source_add2 = sf_add2<FPT, policy::cast_to_safe_policy::equivalent>;

    using target_all = sf_all<FPT, policy::cast_none>;
    using target_of = sf_of<FPT, policy::cast_none>;
    using target_add1 = sf_add1<FPT, policy::cast_none>;
    using target_add2 = sf_add2<FPT, policy::cast_none>;

    BOOST_CHECK((std::is_convertible_v<source_all, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_add1, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_add2, source_all>));

    BOOST_CHECK((not std::is_convertible_v<source_of, target_all>));
    BOOST_CHECK((std::is_convertible_v<source_of, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add1, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add2, source_of>));

    BOOST_CHECK((not std::is_convertible_v<source_add1, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_of>));
    BOOST_CHECK((std::is_convertible_v<source_add1, target_add1>));
    BOOST_CHECK((std::is_convertible_v<source_add1, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_add1>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add1>));

    BOOST_CHECK((not std::is_convertible_v<source_add2, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_of>));
    BOOST_CHECK((std::is_convertible_v<source_add2, target_add1>));
    BOOST_CHECK((std::is_convertible_v<source_add2, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add2>));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(safe_float_cast_to_safe_equivalent_explicit, FPT, test_types) {
    using source_all = sf_all<FPT, policy::cast_to_safe_policy::equivalent_explicit>;
    using source_of = sf_of<FPT, policy::cast_to_safe_policy::equivalent_explicit>;
    using source_add1 = sf_add1<FPT, policy::cast_to_safe_policy::equivalent_explicit>;
    using source_add2 = sf_add2<FPT, policy::cast_to_safe_policy::equivalent_explicit>;

    using target_all = sf_all<FPT, policy::cast_none>;
    using target_of = sf_of<FPT, policy::cast_none>;
    using target_add1 = sf_add1<FPT, policy::cast_none>;
    using target_add2 = sf_add2<FPT, policy::cast_none>;

    BOOST_CHECK((not std::is_convertible_v<source_all, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_add1, source_all>));
    BOOST_CHECK((not std::is_constructible_v<target_add2, source_all>));

    BOOST_CHECK((not std::is_convertible_v<source_of, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add1, source_of>));
    BOOST_CHECK((not std::is_constructible_v<target_add2, source_of>));

    BOOST_CHECK((not std::is_convertible_v<source_add1, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_add1>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add1>));

    BOOST_CHECK((not std::is_convertible_v<source_add2, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_all, source_add2>));
    BOOST_CHECK((not std::is_constructible_v<target_of, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add2>));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(safe_float_cast_to_safe_all, FPT, test_types) {
    using source_all = sf_all<FPT, policy::cast_to_safe_policy::all>;
    using source_of = sf_of<FPT, policy::cast_to_safe_policy::all>;
    using source_add1 = sf_add1<FPT, policy::cast_to_safe_policy::all>;
    using source_add2 = sf_add2<FPT, policy::cast_to_safe_policy::all>;

    using target_all = sf_all<FPT, policy::cast_none>;
    using target_of = sf_of<FPT, policy::cast_none>;
    using target_add1 = sf_add1<FPT, policy::cast_none>;
    using target_add2 = sf_add2<FPT, policy::cast_none>;

    BOOST_CHECK((std::is_convertible_v<source_all, target_all>));
    BOOST_CHECK((std::is_convertible_v<source_all, target_of>));
    BOOST_CHECK((std::is_convertible_v<source_all, target_add1>));
    BOOST_CHECK((std::is_convertible_v<source_all, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_all>));

    BOOST_CHECK((std::is_convertible_v<source_of, target_all>));
    BOOST_CHECK((std::is_convertible_v<source_of, target_of>));
    BOOST_CHECK((std::is_convertible_v<source_of, target_add1>));
    BOOST_CHECK((std::is_convertible_v<source_of, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_of>));

    BOOST_CHECK((std::is_convertible_v<source_add1, target_all>));
    BOOST_CHECK((std::is_convertible_v<source_add1, target_of>));
    BOOST_CHECK((std::is_convertible_v<source_add1, target_add1>));
    BOOST_CHECK((std::is_convertible_v<source_add1, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add1>));

    BOOST_CHECK((std::is_convertible_v<source_add2, target_all>));
    BOOST_CHECK((std::is_convertible_v<source_add2, target_of>));
    BOOST_CHECK((std::is_convertible_v<source_add2, target_add1>));
    BOOST_CHECK((std::is_convertible_v<source_add2, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add2>));
}

BOOST_AUTO_TEST_CASE_TEMPLATE(safe_float_cast_to_safe_all_explicit, FPT, test_types) {
    using source_all = sf_all<FPT, policy::cast_to_safe_policy::all_explicit>;
    using source_of = sf_of<FPT, policy::cast_to_safe_policy::all_explicit>;
    using source_add1 = sf_add1<FPT, policy::cast_to_safe_policy::all_explicit>;
    using source_add2 = sf_add2<FPT, policy::cast_to_safe_policy::all_explicit>;

    using target_all = sf_all<FPT, policy::cast_none>;
    using target_of = sf_of<FPT, policy::cast_none>;
    using target_add1 = sf_add1<FPT, policy::cast_none>;
    using target_add2 = sf_add2<FPT, policy::cast_none>;

    BOOST_CHECK((not std::is_convertible_v<source_all, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_all, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_all>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_all>));

    BOOST_CHECK((not std::is_convertible_v<source_of, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_of, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_of>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_of>));

    BOOST_CHECK((not std::is_convertible_v<source_add1, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_add1, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add1>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add1>));

    BOOST_CHECK((not std::is_convertible_v<source_add2, target_all>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_of>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_add1>));
    BOOST_CHECK((not std::is_convertible_v<source_add2, target_add2>));
    BOOST_CHECK((std::is_constructible_v<target_all, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_of, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add1, source_add2>));
    BOOST_CHECK((std::is_constructible_v<target_add2, source_add2>));
}

BOOST_AUTO_TEST_SUITE_END()


// Copyright (c) 2014
// Akira Takahashi, Fumiki Fukuda.
// Released under the CC0 1.0 Universal license.

#include <type_traits>

struct is_class_impl {
  template <class T>
  static std::true_type check(int T::*);

  template <class T>
  static std::false_type check(...);
};

template <class T>
struct is_class :
  public decltype(is_class_impl::check<T>(nullptr)) {};

#include <iostream>
#include <boost/utility.hpp>

using namespace boost;

template <class T>
void check(T value, typename enable_if<is_class<T>>::type* = 0)
{
  std::cout << "Tはクラス" << std::endl;
}

template <class T>
void check(T value, typename disable_if<is_class<T>>::type* = 0)
{
  std::cout << "Tはクラスではない" << std::endl;
}

struct X {};

int main()
{
  check(X());
  check(3);
}


////////////////////////////////////////////////////////////////////////////////
//  Copyright (c) 2015 Bryce Adelstein Lelbach aka wash
//
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
////////////////////////////////////////////////////////////////////////////////

#include <mdspan>

#include <boost/core/lightweight_test.hpp>

using std::rank;
using std::extent;

using std::experimental::dyn;
using std::experimental::detail::dynamic_extent;

int main()
{
    BOOST_TEST_EQ((dynamic_extent(0)), 0);
    BOOST_TEST_EQ((dynamic_extent(1)), 0);
    BOOST_TEST_EQ((dynamic_extent(2)), 0);

    BOOST_TEST_EQ((dynamic_extent(0, 3)), 3);
    BOOST_TEST_EQ((dynamic_extent(1, 3)), 0);

    BOOST_TEST_EQ((dynamic_extent(0, 0)), 0);
    BOOST_TEST_NE((dynamic_extent(0, 0)), dyn);
    BOOST_TEST_EQ((dynamic_extent(1, 0)), 0);
    BOOST_TEST_NE((dynamic_extent(1, 0)), dyn);

    BOOST_TEST_EQ((dynamic_extent(0, 3, 5)), 3);
    BOOST_TEST_EQ((dynamic_extent(1, 3, 5)), 5);
    BOOST_TEST_EQ((dynamic_extent(2, 3, 5)), 0);

    BOOST_TEST_EQ((dynamic_extent(0, dyn, 5)), dyn);
    BOOST_TEST_NE((dynamic_extent(0, dyn, 5)), 0);
    BOOST_TEST_EQ((dynamic_extent(1, dyn, 5)), 5);
    BOOST_TEST_EQ((dynamic_extent(2, dyn, 5)), 0);

    return boost::report_errors();
}


#define BOOST_TEST_MODULE ReduceByKey
#include <algorithm>
#include <numeric>
#include <boost/test/unit_test.hpp>
#include <vexcl/vector.hpp>
#include <vexcl/reduce_by_key.hpp>
#include "context_setup.hpp"

BOOST_AUTO_TEST_CASE(rbk)
{
    const int n = 1024 * 1024;

    std::vector<int>    x = random_vector<int>   (n);
    std::vector<double> y = random_vector<double>(n);

    std::sort(x.begin(), x.end());

    std::vector<vex::backend::command_queue> queue(1, ctx.queue(0));

    vex::vector<int>    ikeys(queue, x);
    vex::vector<double> ivals(queue, y);

    vex::vector<int>    okeys;
    vex::vector<double> ovals;

    int num_keys = vex::reduce_by_key(ikeys, ivals, okeys, ovals);

    std::vector<int> ux = x;
    ux.erase( std::unique(ux.begin(), ux.end()), ux.end() );

    BOOST_CHECK_EQUAL(ux.size(),    num_keys);
    BOOST_CHECK_EQUAL(okeys.size(), num_keys);
    BOOST_CHECK_EQUAL(ovals.size(), num_keys);

    check_sample(okeys, ovals, [&](size_t, int key, double dev_sum) {
        double host_sum = std::accumulate(
                y.begin() + (std::lower_bound(x.begin(), x.end(), key) - x.begin()),
                y.begin() + (std::upper_bound(x.begin(), x.end(), key) - x.begin()),
                0.0);
        BOOST_CHECK_CLOSE(dev_sum, host_sum, 1e-8);
        });
}

BOOST_AUTO_TEST_CASE(rbk_tuple)
{
    const int n = 1000 * 1000;

    std::vector<cl_int>  k1(n);
    std::vector<cl_long> k2(n);

    {
        std::vector<cl_int>  k1s = random_vector<cl_int> (n);
        std::vector<cl_long> k2s = random_vector<cl_long>(n);

        std::vector<size_t> idx(n);
        std::iota(idx.begin(), idx.end(), 0);

        std::sort(idx.begin(), idx.end(), [&](size_t i, size_t j) {
                return std::make_tuple(k1s[i], k2s[i]) < std::make_tuple(k1s[j], k2s[j]);
                });

        for(int i = 0; i < n; ++i) {
            k1[i] = k1s[idx[i]];
            k2[i] = k2s[idx[i]];
        }
    }

    std::vector<double> y = random_vector<double>(n);

    std::vector<vex::backend::command_queue> queue(1, ctx.queue(0));

    vex::vector<cl_int>  ikey1(queue, k1);
    vex::vector<cl_long> ikey2(queue, k2);
    vex::vector<double>  ivals(queue, y);

    vex::vector<cl_int>  okey1;
    vex::vector<cl_long> okey2;
    vex::vector<double>  ovals;

    VEX_FUNCTION(bool, equal, (cl_int, a1)(cl_long, a2)(cl_int, b1)(cl_long, b2),
            return (a1 == b1) && (a2 == b2);
            );

    VEX_FUNCTION(double, plus, (double, x)(double, y),
            return x + y;
            );

    int num_keys = vex::reduce_by_key(
            boost::fusion::vector_tie(ikey1, ikey2), ivals,
            boost::fusion::vector_tie(okey1, okey2), ovals,
            equal, plus
            );

    std::vector<cl_int>  rk1;
    std::vector<cl_long> rk2;
    std::vector<double>  rsum;

    rk1.reserve(num_keys);
    rk2.reserve(num_keys);
    rsum.reserve(num_keys);

    rk1.push_back(k1[0]);
    rk2.push_back(k2[0]);
    rsum.push_back(y[0]);

    for(int i = 1; i < n; ++i) {
        if (std::make_tuple(k1[i-1], k2[i-1]) == std::make_tuple(k1[i], k2[i])) {
            rsum.back() += y[i];
        } else {
            rk1.push_back(k1[i]);
            rk2.push_back(k2[i]);
            rsum.push_back(y[i]);
        }
    }

    BOOST_CHECK_EQUAL(rsum.size(),  num_keys);
    BOOST_CHECK_EQUAL(okey1.size(), num_keys);
    BOOST_CHECK_EQUAL(okey2.size(), num_keys);
    BOOST_CHECK_EQUAL(ovals.size(), num_keys);

    std::vector<size_t> idx(n);
    std::iota(idx.begin(), idx.end(), 0);

    check_sample(okey1, okey2, ovals, [&](size_t i, cl_int key1, cl_long key2, double dsum) {
        BOOST_CHECK_EQUAL(key1, rk1[i]);
        BOOST_CHECK_EQUAL(key2, rk2[i]);
        BOOST_CHECK_CLOSE(dsum, rsum[i], 1e-8);
        });
}

BOOST_AUTO_TEST_SUITE_END()


#include <ros/ros.h>
#include <tf/transform_broadcaster.h>
#include <tf/transform_listener.h>
#include <tf/transform_datatypes.h>
#include <Eigen/Geometry>
#include <tf_conversions/tf_eigen.h>

int main(int argc, char** argv)
{
  // Define ROS info
  ros::init(argc, argv, "extrinsic_calibration");
  ros::NodeHandle nh;
  static tf::TransformBroadcaster camera_broadcaster;
  static tf::TransformBroadcaster target_broadcaster;
  static tf::TransformListener listener;

  // Get ROS params
  bool sim_robot;
  double targ_x, targ_y, targ_z;
  std::string camera_link;
  nh.param<bool>("calibration_node/sim_robot", sim_robot, true);
  nh.param<double>("calibration_node/targ_x", targ_x, 0.3048);
  nh.param<double>("calibration_node/targ_y", targ_y, 0.0);
  nh.param<double>("calibration_node/targ_z", targ_z, 0.0);
  nh.param<std::string>("calibration_node/camera_link", camera_link, "camera_link");

  // Update location of AR tag
  tf::Transform targ_transform;
  tf::Quaternion targ_q;
  if (!sim_robot)
  {
    ROS_INFO("Move robot to target to calibration location");
  }
  else{
    targ_q.setRPY(0, 0, 0);
    targ_transform.setOrigin(tf::Vector3(targ_x, targ_y, targ_z));
    targ_transform.setRotation(targ_q);
  }
  ROS_INFO("Sending target transform");
  target_broadcaster.sendTransform(
        tf::StampedTransform(targ_transform, ros::Time::now(), "base_link", "target_link"));
  ros::Duration(1.0).sleep();

  // Broadcast initial camera transform that is based on target_link
  tf::Transform transform;
  tf::Quaternion q;
  q.setRPY(0,0,0);
  transform.setOrigin(tf::Vector3(0,0,0));
  transform.setRotation(q);
  camera_broadcaster.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "target_link", camera_link));
  tf::StampedTransform stamped;
  ROS_INFO("Sending initial camera transform");
  ros::Duration(1.0).sleep();

  // Calibrate the camera position
  bool calibrating = true;

  while (calibrating)
  {
   int iters = 60;
   double rx = 0;
   double ry = 0;
   double rz = 0;
   double rw = 0;
   double x = 0;
   double y = 0;
   double z = 0;
    for (int ind = 0; ind < iters; ind++)
    {
      target_broadcaster.sendTransform(
          tf::StampedTransform(targ_transform, ros::Time::now(), "base_link", "target_link"));
      try
      {
        // Get actual location of target in camera frame
        listener.lookupTransform("/ar_marker_0", camera_link, ros::Time(0), stamped);
        // Update location of camera based on that transform
        stamped.frame_id_ = "target_link";
        stamped.child_frame_id_ = camera_link;
        camera_broadcaster.sendTransform(stamped);

        // Take average
        // Note: There are better ways to average quaternions (https://stackoverflow.com/questions/12374087/average-of-multiple-quaternions)
        rx += stamped.getRotation().x()/float(iters);
        ry += stamped.getRotation().y()/float(iters);
        rz += stamped.getRotation().z()/float(iters);
        rw += stamped.getRotation().w()/float(iters);
        x += stamped.getOrigin().x()/float(iters);
        y += stamped.getOrigin().y()/float(iters);
        z += stamped.getOrigin().z()/float(iters);
      }
      catch (tf::TransformException ex)
      {
        ROS_ERROR("%s", ex.what());
        ros::Duration(1.0).sleep();
      }

      ros::spinOnce();
      ros::Duration(0.25).sleep();
    }

    // Update location of camera based on the average transform
    stamped.frame_id_ = "target_link";
    stamped.child_frame_id_ = camera_link;
    stamped.setRotation(tf::Quaternion(rx,ry,rz,rw).normalize());
    stamped.setOrigin(tf::Vector3(x,y,z));
    camera_broadcaster.sendTransform(stamped);

    // Get transform from robot base to the camera and display it for the user to copy.
    // Ideally, this would automatically be written to a file in a real system
    listener.lookupTransform("/base_link", camera_link, ros::Time(0), stamped);
    double yaw, pitch, roll;
    stamped.getBasis().getRPY(roll, pitch, yaw);
    ROS_ERROR("Calibrated camera location in base_link frame. Update these values in launch file");
    std::cout << "Link name: " << camera_link << '\n';
    std::cout << stamped.getOrigin()[0] << " " << stamped.getOrigin()[1] << " "
              << stamped.getOrigin()[2] << " " << yaw << " "
              << pitch << " " << roll << "\n";

    ROS_ERROR(" Exit? y/n");
    char input;
    std::cin >> input;
    if (input == 'y')
    {
      calibrating = false;
    }
  }
}