In a project of mine I used to transform coordinate values from Kernel::FT to CGAL::Gmpq and back. The code does not compile anymore with the same kernel choice as before, namely
typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
But with the alternative I tried without being sure of it being completely equivalent to the former one everything compiled without errors:
typedef CGAL::Cartesian< CGAL::Lazy_exact_nt<CGAL::Gmpq> > Kernel;
My header definitions
// the following kernel gives no error
//typedef CGAL::Cartesian< CGAL::Lazy_exact_nt<CGAL::Gmpq> > Kernel;
typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
typedef Kernel::Point_2 Point_2;
typedef Kernel::Circle_2 Circle_2;
typedef Kernel::Line_2 Line_2;
typedef CGAL::Gps_circle_segment_traits_2<Kernel> Traits_2;
typedef CGAL::General_polygon_set_2<Traits_2> Polygon_set_2;
typedef Traits_2::General_polygon_2 Polygon_2;
typedef Traits_2::General_polygon_with_holes_2 Polygon_with_holes_2;
typedef Traits_2::Curve_2 Curve_2;
typedef Traits_2::X_monotone_curve_2 X_monotone_curve_2;
typedef Traits_2::Point_2 Point_2t;
typedef Traits_2::CoordNT coordnt;
typedef CGAL::Arrangement_2<Traits_2> Arrangement_2;
typedef Arrangement_2::Face_handle Face_handle;
The code that fails
CGAL::Gmpfr convert(CGAL::Gmpq z)
{
CGAL::Gmpz num = z.numerator();
CGAL::Gmpz den = z.denominator();
CGAL::Gmpfr num_f(num);
CGAL::Gmpfr den_f(den);
return num_f/den_f;
}
CGAL::Gmpfr convert(Traits_2::CoordNT z, int use_precision)
{
Kernel::FT a0_val = z.a0();
Kernel::FT a1_val = z.a1();
Kernel::FT root_val = z.root();
//the following three lines give errors
CGAL::Gmpq a0_q = a0_val.exact();
CGAL::Gmpq a1_q = a1_val.exact();
CGAL::Gmpq root_q = root_val.exact();
CGAL::Gmpfr a0_f = convert(a0_q);
CGAL::Gmpfr a1_f = convert(a1_q);
CGAL::Gmpfr root_f = convert(root_q);
CGAL::Gmpfr res = a0_f + a1_f * root_f.sqrt(use_precision);
return res;
}
Point_2 convert(Point_2t p)
{
CGAL::Gmpfr xx = convert(p.x());
CGAL::Gmpfr yy = convert(p.y());
CGAL::Gmpq xx1 = xx;
CGAL::Gmpq yy1 = yy;
// the following two lines give errors
Kernel::FT xx2 = xx1;
Kernel::FT yy2 = yy1;
Point_2 pp(xx2, yy2);
return pp;
}
The errors (the first three and the last two are essential identical):
cgal.cpp:48:32: error: conversion from ‘CGAL::Lazy<CGAL::Interval_nt<false>, boost::multiprecision::number<boost::multiprecision::backends::gmp_rational>, CGAL::To_interval<boost::multiprecision::number<boost::multiprecision::backends::gmp_rational> > >::ET {aka boost::multiprecision::number<boost::multiprecision::backends::gmp_rational>}’ to non-scalar type ‘CGAL::Gmpq’ requested
CGAL::Gmpq a0_q = a0_val.exact();
~~~~~~~~~~~~^~
cgal.cpp:49:32: error: conversion from ‘CGAL::Lazy<CGAL::Interval_nt<false>, boost::multiprecision::number<boost::multiprecision::backends::gmp_rational>, CGAL::To_interval<boost::multiprecision::number<boost::multiprecision::backends::gmp_rational> > >::ET {aka boost::multiprecision::number<boost::multiprecision::backends::gmp_rational>}’ to non-scalar type ‘CGAL::Gmpq’ requested
CGAL::Gmpq a1_q = a1_val.exact();
~~~~~~~~~~~~^~
cgal.cpp:50:36: error: conversion from ‘CGAL::Lazy<CGAL::Interval_nt<false>, boost::multiprecision::number<boost::multiprecision::backends::gmp_rational>, CGAL::To_interval<boost::multiprecision::number<boost::multiprecision::backends::gmp_rational> > >::ET {aka boost::multiprecision::number<boost::multiprecision::backends::gmp_rational>}’ to non-scalar type ‘CGAL::Gmpq’ requested
CGAL::Gmpq root_q = root_val.exact();
~~~~~~~~~~~~~~^~
cgal.cpp: In function ‘Point_2 convert(Point_2t)’:
cgal.cpp:71:19: error: conversion from ‘CGAL::Gmpq’ to non-scalar type ‘CGAL::Lazy_kernel_generic_base<CGAL::Simple_cartesian<boost::multiprecision::number<boost::multiprecision::backends::gmp_rational> >, CGAL::Simple_cartesian<CGAL::Interval_nt<false> >, CGAL::Cartesian_converter<CGAL::Simple_cartesian<boost::multiprecision::number<boost::multiprecision::backends::gmp_rational> >, CGAL::Simple_cartesian<CGAL::Interval_nt<false> > >, CGAL::Epeck>::FT {aka CGAL::Lazy_exact_nt<boost::multiprecision::number<boost::multiprecision::backends::gmp_rational> >}’ requested
Kernel::FT xx2 = xx1;
^~~
cgal.cpp:72:19: error: conversion from ‘CGAL::Gmpq’ to non-scalar type ‘CGAL::Lazy_kernel_generic_base<CGAL::Simple_cartesian<boost::multiprecision::number<boost::multiprecision::backends::gmp_rational> >, CGAL::Simple_cartesian<CGAL::Interval_nt<false> >, CGAL::Cartesian_converter<CGAL::Simple_cartesian<boost::multiprecision::number<boost::multiprecision::backends::gmp_rational> >, CGAL::Simple_cartesian<CGAL::Interval_nt<false> > >, CGAL::Epeck>::FT {aka CGAL::Lazy_exact_nt<boost::multiprecision::number<boost::multiprecision::backends::gmp_rational> >}’ requested
Kernel::FT yy2 = yy1;
It seems that the default kernel CGAL::Exact_predicates_exact_constructions_kernel
now has a coordinate number type related to "boost-something" and this is not compatible to CGAL::Gmpq
anymore.
What is the best solution to the problem, given that I want to use the fastest kernel equivalent to a CGAL::Exact_predicates_exact_constructions_kernel
?
Shall I switch to CGAL::Cartesian< CGAL::Lazy_exact_nt<CGAL::Gmpq> >
or even to CGAL::Extended_cartesian< CGAL::Lazy_exact_nt<CGAL::Gmpq> >
?
Or can I set a certain environment variable so that CGAL::Exact_predicates_exact_construction_kernel works with CGAL::Gmpq by default instead of "boost-something", provided this does not make things slower?
Or can I just rewrite the offending lines with some newly introduced conversion operators/functions in CGAL 5.2.1. I am at the moment not aware of?
If you look at CGAL/Exact_predicates_exact_constructions_kernel.h
to see how Epeck is defined, you can define you own version of Epeck using Gmpq as
class Kernel
: public Type_equality_wrapper<
Lazy_kernel_base< Simple_cartesian<Gmpq>,
Simple_cartesian<Interval_nt_advanced>,
Cartesian_converter< Simple_cartesian<Gmpq>,
Simple_cartesian<Interval_nt_advanced> >,
Kernel>,
Kernel >
{};
(you may need to add CGAL::
all over)
The rational type chosen by default depends on what is available, it can come from boost.Multiprecision (you can disable that choice with CGAL_DO_NOT_USE_BOOST_MP). It can come from GMPXX if CGAL_USE_GMPXX is defined. It can also be Gmpq (CGAL_USE_GMP but not CGAL_USE_GMPXX), leda_rational, etc.
I understand this variability may not be ideal for not-so-usual cases like yours where you care about the type...
I have never used Gmpfr, but it should not be hard to add constructors from mpz_class and mpz_int similar to the one from Gmpz (do you want to file an enhancement request on github, or possibly write it yourself? It may be a 1-liner, not counting the #ifdef
). And then it would likely be possible to write your convertor in a way that is at least portable to all the GMP-based rationals, maybe using Fraction_traits<FT>::Decompose
, although that will do an unnecessary copy. Actually, it may even make sense to add constructors to Gmpfr from rational types directly using mpfr_set_q.