// Copyright (c) 2017 // INRIA Saclay-Ile de France (France), // // This file is part of CGAL (www.cgal.org) // // $URL: https://github.com/CGAL/cgal/blob/v5.1/Number_types/include/CGAL/boost_mp.h $ // $Id: boost_mp.h 52164b1 2019-10-19T15:34:59+02:00 Sébastien Loriot // SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial // // Author(s) : Marc Glisse #ifndef CGAL_BOOST_MP_H #define CGAL_BOOST_MP_H #include // This could check BOOST_VERSION >= 105300, but before 1.56 there is no // implicit conversion from double, which makes it hard to use in CGAL. // It is easier to disable this number type completely for old versions. // Before 1.63, I/O is broken. Again, disabling the whole file is just the // easy solution. // TODO: MSVC has trouble with versions <= 1.69, reenable once 1.70 has been // tested. https://github.com/boostorg/multiprecision/issues/98 #if !defined CGAL_DISABLE_GMP && !defined CGAL_DO_NOT_USE_BOOST_MP && BOOST_VERSION >= 106300 && !defined _MSC_VER #define CGAL_USE_BOOST_MP 1 #include // *ary_function #include #include // We can't just include all Boost.Multiprecision here... #include #include // ... but we kind of have to :-( #include #ifdef CGAL_USE_GMP // Same dance as in CGAL/gmp.h # include # if defined(BOOST_MSVC) # pragma warning(push) # pragma warning(disable: 4127 4244 4146 4267) // conversion with loss of data // warning on - applied on unsigned number # endif # include # if defined(BOOST_MSVC) # pragma warning(pop) # endif # include #endif #ifdef CGAL_USE_MPFR # include #endif // TODO: work on the coercions (end of the file) namespace CGAL { // Algebraic_structure_traits template ::value> > struct AST_boost_mp; template struct AST_boost_mp > : Algebraic_structure_traits_base< NT, Euclidean_ring_tag > { typedef NT Type; typedef Euclidean_ring_tag Algebraic_category; typedef Boolean_tag::is_exact> Is_exact; typedef Tag_false Is_numerical_sensitive; struct Is_zero: public CGAL::cpp98::unary_function { bool operator()( const Type& x) const { return x.is_zero(); } }; struct Div: public CGAL::cpp98::binary_function { template Type operator()(const T& x, const U& y) const { return x / y; } }; struct Mod: public CGAL::cpp98::binary_function { template Type operator()(const T& x, const U& y) const { return x % y; } }; struct Gcd : public CGAL::cpp98::binary_function { template Type operator()(const T& x, const U& y) const { return boost::multiprecision::gcd(x, y); } }; struct Sqrt : public CGAL::cpp98::unary_function { template Type operator()(const T& x) const { return boost::multiprecision::sqrt(x); } }; }; template struct AST_boost_mp > : public Algebraic_structure_traits_base< NT , Field_tag > { public: typedef NT Type; typedef Field_tag Algebraic_category; typedef Tag_true Is_exact; typedef Tag_false Is_numerical_sensitive; struct Is_zero: public CGAL::cpp98::unary_function { bool operator()( const Type& x) const { return x.is_zero(); } }; struct Div: public CGAL::cpp98::binary_function { template Type operator()(const T& x, const U& y) const { return x / y; } }; }; template struct Algebraic_structure_traits > : AST_boost_mp > {}; template struct Algebraic_structure_traits > : Algebraic_structure_traits::result_type > {}; // Real_embeddable_traits template struct RET_boost_mp_base : public INTERN_RET::Real_embeddable_traits_base< NT , CGAL::Tag_true > { typedef NT Type; struct Is_zero: public CGAL::cpp98::unary_function { bool operator()( const Type& x) const { return x.is_zero(); } }; struct Is_positive: public CGAL::cpp98::unary_function { bool operator()( const Type& x) const { return x.sign() > 0; } }; struct Is_negative: public CGAL::cpp98::unary_function { bool operator()( const Type& x) const { return x.sign() < 0; } }; struct Abs : public CGAL::cpp98::unary_function { template Type operator()(const T& x) const { return boost::multiprecision::abs(x); } }; struct Sgn : public CGAL::cpp98::unary_function { ::CGAL::Sign operator()(Type const& x) const { return CGAL::sign(x.sign()); } }; struct Compare : public CGAL::cpp98::binary_function { Comparison_result operator()(const Type& x, const Type& y) const { return CGAL::sign(x.compare(y)); } }; struct To_double : public CGAL::cpp98::unary_function { double operator()(const Type& x) const { return x.template convert_to(); } }; struct To_interval : public CGAL::cpp98::unary_function< Type, std::pair< double, double > > { std::pair operator()(const Type& x) const { // See if https://github.com/boostorg/multiprecision/issues/108 suggests anything better // assume the conversion is within 1 ulp // adding IA::smallest() doesn't work because inf-e=inf, even rounded down. double i = x.template convert_to(); double s = i; double inf = std::numeric_limits::infinity(); int cmp = x.compare(i); if (cmp > 0) { s = nextafter(s, +inf); CGAL_assertion(x.compare(s) < 0); } else if (cmp < 0) { i = nextafter(i, -inf); CGAL_assertion(x.compare(i) > 0); } return std::pair (i, s); } }; }; template ::value> > struct RET_boost_mp; template struct RET_boost_mp > : RET_boost_mp_base {}; template struct RET_boost_mp > : RET_boost_mp_base {}; #ifdef CGAL_USE_MPFR // Because of these full specializations, things get instantiated more eagerly. Make them artificially partial if necessary. template <> struct RET_boost_mp : RET_boost_mp_base { typedef boost::multiprecision::mpz_int Type; struct To_interval : public CGAL::cpp98::unary_function< Type, std::pair< double, double > > { std::pair operator()(const Type& x) const { #if MPFR_VERSION_MAJOR >= 3 MPFR_DECL_INIT (y, 53); /* Assume IEEE-754 */ int r = mpfr_set_z (y, x.backend().data(), MPFR_RNDA); double i = mpfr_get_d (y, MPFR_RNDA); /* EXACT but can overflow */ if (r == 0 && is_finite (i)) return std::pair(i, i); else { double s = nextafter (i, 0); if (i < 0) return std::pair(i, s); else return std::pair(s, i); } #else mpfr_t y; mpfr_init2 (y, 53); /* Assume IEEE-754 */ mpfr_set_z (y, x.backend().data(), GMP_RNDD); double i = mpfr_get_d (y, GMP_RNDD); /* EXACT but can overflow */ mpfr_set_z (y, x.backend().data(), GMP_RNDU); double s = mpfr_get_d (y, GMP_RNDU); /* EXACT but can overflow */ mpfr_clear (y); return std::pair(i, s); #endif } }; }; template <> struct RET_boost_mp : RET_boost_mp_base { typedef boost::multiprecision::mpq_rational Type; struct To_interval : public CGAL::cpp98::unary_function< Type, std::pair< double, double > > { std::pair operator()(const Type& x) const { # if MPFR_VERSION_MAJOR >= 3 mpfr_exp_t emin = mpfr_get_emin(); mpfr_set_emin(-1073); MPFR_DECL_INIT (y, 53); /* Assume IEEE-754 */ int r = mpfr_set_q (y, x.backend().data(), MPFR_RNDA); r = mpfr_subnormalize (y, r, MPFR_RNDA); /* Round subnormals */ double i = mpfr_get_d (y, MPFR_RNDA); /* EXACT but can overflow */ mpfr_set_emin(emin); /* Restore old value, users may care */ // With mpfr_set_emax(1024) we could drop the is_finite test if (r == 0 && is_finite (i)) return std::pair(i, i); else { double s = nextafter (i, 0); if (i < 0) return std::pair(i, s); else return std::pair(s, i); } # else mpfr_t y; mpfr_init2 (y, 53); /* Assume IEEE-754 */ mpfr_set_q (y, x.backend().data(), GMP_RNDD); double i = mpfr_get_d (y, GMP_RNDD); /* EXACT but can overflow */ mpfr_set_q (y, x.backend().data(), GMP_RNDU); double s = mpfr_get_d (y, GMP_RNDU); /* EXACT but can overflow */ mpfr_clear (y); return std::pair(i, s); # endif } }; }; #endif template struct Real_embeddable_traits > : RET_boost_mp > {}; template struct Real_embeddable_traits > : Real_embeddable_traits::result_type > {}; // Modular_traits template ::value> > struct MT_boost_mp { typedef T NT; typedef ::CGAL::Tag_false Is_modularizable; typedef ::CGAL::Null_functor Residue_type; typedef ::CGAL::Null_functor Modular_image; typedef ::CGAL::Null_functor Modular_image_representative; }; template struct MT_boost_mp > { typedef T NT; typedef CGAL::Tag_true Is_modularizable; typedef Residue Residue_type; struct Modular_image{ Residue_type operator()(const NT& a){ NT tmp(CGAL::mod(a,NT(Residue::get_current_prime()))); return CGAL::Residue(tmp.template convert_to()); } }; struct Modular_image_representative{ NT operator()(const Residue_type& x){ return NT(x.get_value()); } }; }; template struct Modular_traits > : MT_boost_mp > {}; template struct Modular_traits > : Modular_traits::result_type > {}; // Split_double template ::value> > struct SD_boost_mp { void operator()(double d, NT &num, NT &den) const { num = d; den = 1; } }; template struct SD_boost_mp > { void operator()(double d, NT &num, NT &den) const { std::pair p = split_numerator_denominator(d); num = NT(p.first); den = NT(p.second); } }; template struct Split_double > : SD_boost_mp > {}; template struct Split_double > : Split_double::result_type > {}; // Fraction_traits template ::value> > struct FT_boost_mp { typedef T Type; typedef Tag_false Is_fraction; typedef Null_tag Numerator_type; typedef Null_tag Denominator_type; typedef Null_functor Common_factor; typedef Null_functor Decompose; typedef Null_functor Compose; }; template struct FT_boost_mp > { typedef NT Type; typedef ::CGAL::Tag_true Is_fraction; typedef typename boost::multiprecision::component_type::type Numerator_type; typedef Numerator_type Denominator_type; typedef typename Algebraic_structure_traits< Numerator_type >::Gcd Common_factor; class Decompose { public: typedef Type first_argument_type; typedef Numerator_type& second_argument_type; typedef Denominator_type& third_argument_type; void operator () ( const Type& rat, Numerator_type& num, Denominator_type& den) { num = numerator(rat); den = denominator(rat); } }; class Compose { public: typedef Numerator_type first_argument_type; typedef Denominator_type second_argument_type; typedef Type result_type; Type operator ()( const Numerator_type& num , const Denominator_type& den ) { return Type(num, den); } }; }; template struct Fraction_traits > : FT_boost_mp > {}; template struct Fraction_traits > : Fraction_traits::result_type > {}; // Coercions namespace internal { namespace boost_mp { BOOST_MPL_HAS_XXX_TRAIT_DEF(type); } } template struct Coercion_traits, boost::multiprecision::number > { typedef boost::common_type, boost::multiprecision::number > CT; typedef Boolean_tag::value> Are_implicit_interoperable; // FIXME: the implicit/explicit answers shouldn't be the same... typedef Are_implicit_interoperable Are_explicit_interoperable; // FIXME: won't compile when they are not interoperable. typedef typename CT::type Type; struct Cast{ typedef Type result_type; template Type operator()(const U& x) const { return Type(x); } }; }; // Avoid ambiguity with the specialization for ... template struct Coercion_traits, boost::multiprecision::number > { typedef boost::multiprecision::number Type; typedef Tag_true Are_implicit_interoperable; typedef Tag_true Are_explicit_interoperable; struct Cast{ typedef Type result_type; template Type operator()(const U& x) const { return Type(x); } }; }; template struct Coercion_traits < boost::multiprecision::detail::expression, boost::multiprecision::detail::expression > : Coercion_traits < typename boost::multiprecision::detail::expression::result_type, typename boost::multiprecision::detail::expression::result_type> { }; // Avoid ambiguity with the specialization for ... template struct Coercion_traits < boost::multiprecision::detail::expression, boost::multiprecision::detail::expression > : Coercion_traits < typename boost::multiprecision::detail::expression::result_type, typename boost::multiprecision::detail::expression::result_type> { }; template struct Coercion_traits, boost::multiprecision::detail::expression > : Coercion_traits < boost::multiprecision::number, typename boost::multiprecision::detail::expression::result_type> { }; template struct Coercion_traits, boost::multiprecision::number > : Coercion_traits < typename boost::multiprecision::detail::expression::result_type, boost::multiprecision::number > { }; // TODO: fix existing coercions // (double -> rational is implicit only for 1.56+, see ticket #10082) // The real solution would be to avoid specializing Coercion_traits for all pairs of number types and let it auto-detect what works, so only broken types need an explicit specialization. // Ignore types smaller than long #define CGAL_COERCE_INT(int) \ template \ struct Coercion_traits, int> { \ typedef boost::multiprecision::number Type; \ typedef Tag_true Are_implicit_interoperable; \ typedef Tag_true Are_explicit_interoperable; \ struct Cast{ \ typedef Type result_type; \ template Type operator()(const U& x) const { return Type(x); } \ }; \ }; \ template \ struct Coercion_traits > \ : Coercion_traits, int> {}; \ template \ struct Coercion_traits, int> \ : Coercion_traits::result_type, int>{}; \ template \ struct Coercion_traits > \ : Coercion_traits::result_type, int>{} CGAL_COERCE_INT(short); CGAL_COERCE_INT(int); CGAL_COERCE_INT(long); #undef CGAL_COERCE_INT // Ignore bounded-precision rationals #define CGAL_COERCE_FLOAT(float) \ template \ struct Coercion_traits, float> { \ typedef boost::multiprecision::number Type; \ typedef Boolean_tag::value != boost::multiprecision::number_kind_integer> Are_implicit_interoperable; \ typedef Are_implicit_interoperable Are_explicit_interoperable; \ struct Cast{ \ typedef Type result_type; \ template Type operator()(const U& x) const { return Type(x); } \ }; \ }; \ template \ struct Coercion_traits > \ : Coercion_traits, float> {}; \ template \ struct Coercion_traits, float> \ : Coercion_traits::result_type, float>{}; \ template \ struct Coercion_traits > \ : Coercion_traits::result_type, float>{} CGAL_COERCE_FLOAT(float); CGAL_COERCE_FLOAT(double); #undef CGAL_COERCE_FLOAT // Because of https://github.com/boostorg/multiprecision/issues/29 , this is not perfect and fails to read some KDS files. template <> class Input_rep : public IO_rep_is_specialized { boost::multiprecision::cpp_rational& q; public: Input_rep(boost::multiprecision::cpp_rational& qq) : q(qq) {} std::istream& operator()(std::istream& in) const { internal::read_float_or_quotient(in, q); return in; } }; #ifdef CGAL_USE_GMP template <> class Input_rep : public IO_rep_is_specialized { boost::multiprecision::mpq_rational& q; public: Input_rep(boost::multiprecision::mpq_rational& qq) : q(qq) {} std::istream& operator()(std::istream& in) const { internal::read_float_or_quotient(in, q); return in; } }; #endif // Copied from leda_rational.h namespace internal { // See: Stream_support/include/CGAL/IO/io.h template void read_float_or_quotient(std::istream & is, ET& et); template <> inline void read_float_or_quotient(std::istream & is, boost::multiprecision::cpp_rational& et) { internal::read_float_or_quotient(is, et); } #ifdef CGAL_USE_GMP template <> inline void read_float_or_quotient(std::istream & is, boost::multiprecision::mpq_rational& et) { internal::read_float_or_quotient(is, et); } #endif } // namespace internal } //namespace CGAL #include #endif // BOOST_VERSION #endif