Copy ranges defined by iterator pairs inside Lazy objects.

This commit is contained in:
Marc Glisse 2019-02-09 22:31:31 +01:00
parent 6e579bb11a
commit 15d2b82ff8
2 changed files with 141 additions and 121 deletions

View File

@ -62,6 +62,99 @@ namespace internal {
};
}
// Whenever a construction takes iterator pairs as input, whether they point to double of Lazy objects, copy the ranges inside the lazy result so they are available for update_exact(). We analyze the input to try and guess where iterator pairs are. I would prefer if each functor had a specific signature (no overload in this layer) so we wouldn't have to guess.
// FIXME: Lazy_construct_nt is also a construction and needs the same treatment.
namespace Lazy_internal {
template<class...>struct typelist{};
template<int>struct arg_i{};
template<int>struct arg_i_begin{};
template<int>struct arg_i_end{};
template<int>struct arg_i_ip1_range{};
template<class,class,class,class=void>struct analyze_args;
template<class T,class U>struct analyze_args<T,U,typelist<>> {
typedef T creator;
typedef U reader;
};
template<class...T,class...U,class V,class...W>
struct analyze_args<typelist<T...>,typelist<U...>,typelist<V,W...>,std::enable_if_t<!is_iterator_type<V,std::input_iterator_tag>::value>> :
analyze_args<typelist<T...,arg_i<sizeof...(U)>>,typelist<U...,arg_i<sizeof...(T)>>,typelist<W...>> {};
template<class...T,class...U,class It,class...W>
struct analyze_args<typelist<T...>,typelist<U...>,typelist<It,It,W...>,std::enable_if_t<is_iterator_type<It,std::input_iterator_tag>::value>> :
analyze_args<typelist<T...,arg_i_ip1_range<sizeof...(U)>>,typelist<U...,arg_i_begin<sizeof...(T)>,arg_i_end<sizeof...(T)>>,typelist<W...>> {};
template<class...T> using analyze_args_for_lazy = analyze_args<typelist<>,typelist<>,typelist<T...>>;
template<class,class>struct extract1;
template<int i,class T>struct extract1<arg_i<i>,T>:std::tuple_element<i,T>{};
template<int i,class T>struct extract1<arg_i_ip1_range<i>,T>{
typedef std::tuple_element_t<i,T> E;
typedef std::remove_cv_t<std::remove_reference_t<E>> It;
typedef typename std::iterator_traits<It>::value_type element_type;
// TODO: find a way to use an array of the right size, at least for the most frequent constructions
typedef std::vector<element_type> type;
};
template<int i,class...T>decltype(auto)
do_extract(arg_i<i>,std::tuple<T...>const&t)
{return std::get<i>(t);}
template<int i,class...T>decltype(auto)
do_extract(arg_i_begin<i>,std::tuple<T...>const&t)
{return std::begin(std::get<i>(t));}
template<int i,class...T>decltype(auto)
do_extract(arg_i_end<i>,std::tuple<T...>const&t)
{return std::end(std::get<i>(t));}
template<int i,class...T>decltype(auto)
do_extract(arg_i_ip1_range<i>,std::tuple<T...>const&t)
{
typedef std::tuple<T...> L;
typedef std::tuple_element_t<i,L> E;
typedef std::remove_cv_t<std::remove_reference_t<E>> It;
typedef typename std::iterator_traits<It>::value_type element_type;
typedef std::vector<element_type> type;
return type(std::get<i>(t),std::get<i+1>(t));
}
template<class,class>struct data_from_input;
template<class...T,class U>struct data_from_input<typelist<T...>,U> {
typedef std::tuple<typename extract1<T,U>::type...> type;
};
}
template<typename AT, typename ET, typename AC, typename EC, typename E2A, typename...L>
class Lazy_rep_XXX :
public Lazy_rep< AT, ET, E2A >, private EC
{
// Lazy_rep_0 does not inherit from EC or take a parameter AC. It has different constructors.
static_assert(sizeof...(L)>0, "Use Lazy_rep_0 instead");
template <class Ei, class Ai, class E2Ai, class Ki> friend class Lazy_kernel_base;
typedef Lazy_internal::analyze_args_for_lazy<L...> Args;
// How to go from l to Lazy_rep's data
typedef typename Args::creator Creator;
// How to go back
typedef typename Args::reader Reader;
// what Lazy_rep should store
typedef typename Lazy_internal::data_from_input<Creator, std::tuple<L...>>::type LL;
mutable LL l; // L...l; is not yet allowed.
const EC& ec() const { return *this; }
template<class...T>
void update_exact_helper(Lazy_internal::typelist<T...>) const {
this->et = new ET(ec()( CGAL::exact( Lazy_internal::do_extract(T{},l) ) ... ) );
this->at = E2A()(*(this->et));
l = LL(); // There should be a nicer way to clear. Destruction for instance. With this->et as a witness of whether l has already been destructed.
}
public:
void update_exact() const {
update_exact_helper(Reader{});
}
template<class...LL>
Lazy_rep_XXX(const AC& ac, const EC& ec, LL const&...ll) :
Lazy_rep_XXX(Creator{},ac,ec,std::forward_as_tuple(ll...),ll...){};
private:
// Currently we construct the vectors, then move them into the tuple. It would be nicer to construct them in their final destination, because eventually we will also have arrays instead of vectors.
template<class...T,class LLL,class...LL>
Lazy_rep_XXX(Lazy_internal::typelist<T...>, const AC& ac, const EC& ec, LLL const&lll, LL const&...ll) :
Lazy_rep<AT, ET, E2A>(ac(CGAL::approx(ll)...)), EC(ec), l(Lazy_internal::do_extract(T{},lll)...)
{
//this->set_depth(std::max({ -1, (int)CGAL::depth(ll)...}) + 1);
this->set_depth(1); // FIXME: now that we have ranges, we could actually compute the depth if we cared...
}
// TODO: print_dag needs a specific implementation for Lazy_rep_XXX
};
template<typename T, typename LK>
struct Lazy_construction2 {
static const bool Protection = true;
@ -87,7 +180,7 @@ struct Lazy_construction2 {
CGAL_BRANCH_PROFILER(std::string(" failures/calls to : ") + std::string(CGAL_PRETTY_FUNCTION), tmp);
Protect_FPU_rounding<Protection> P;
try {
return new Lazy_rep_n<AT, ET, AC, EC, E2A, L...>(ac, ec, l...);
return new Lazy_rep_XXX<AT, ET, AC, EC, E2A, L...>(ac, ec, l...);
} catch (Uncertain_conversion_exception&) {
CGAL_BRANCH_PROFILER_BRANCH(tmp);
Protect_FPU_rounding<!Protection> P2(CGAL_FE_TONEAREST);
@ -95,108 +188,12 @@ struct Lazy_construction2 {
}
}
// FIXME: this forces us to have default constructors for all types, try to make its instantiation lazier
// Actually, that may be the clearing in update_exact().
result_type operator()() const
{
return new Lazy_rep_0<AT,ET,E2A>();
}
};
#if 0
// Experiment how we can store ranges
template<typename AT, typename ET, typename AC, typename EC, typename E2A, typename T>
class Lazy_rep_XXX :
public Lazy_rep< AT, ET, E2A >, private EC
{
int ld;
mutable std::vector<T> lr;
const EC& ec() const { return *this; }
public:
void update_exact() const {
this->et = new ET(ec()(ld, CGAL::exact(std::begin(lr)), CGAL::exact(std::end(lr))));
this->at = E2A()(*(this->et));
lr = std::vector<T>(); // lr.clear(); lr.shrink_to_fit(); generates worse code
}
template<class Iter>
Lazy_rep_XXX(const AC& ac, const EC& ec, int d, Iter const& f, Iter const& e) :
Lazy_rep<AT, ET, E2A>(ac(d, CGAL::approx(f), CGAL::approx(e))), EC(ec), ld(d), lr(f, e)
{
this->set_depth(1); // ??? Who cares
}
};
template<typename AT, typename ET, typename AC, typename EC, typename E2A, typename T, typename L>
class Lazy_rep_YYY :
public Lazy_rep< AT, ET, E2A >, private EC
{
mutable std::vector<T> lr;
mutable L l;
const EC& ec() const { return *this; }
public:
void update_exact() const {
this->et = new ET(ec()(CGAL::exact(std::begin(lr)), CGAL::exact(std::end(lr)), CGAL::exact(l)));
this->at = E2A()(*(this->et));
lr = std::vector<T>(); // lr.clear(); lr.shrink_to_fit(); generates worse code
l = L();
}
template<class Iter>
Lazy_rep_YYY(const AC& ac, const EC& ec, Iter const& f, Iter const& e, L const& ll) :
Lazy_rep<AT, ET, E2A>(ac(CGAL::approx(f), CGAL::approx(e), CGAL::approx(ll))), EC(ec), lr(f, e), l(ll)
{
this->set_depth(1); // ??? Who cares
}
};
template<typename LK>
struct Lazy_construction2<Construct_ttag<Point_tag>, LK> {
static const bool Protection = true;
typedef Construct_ttag<Point_tag> T;
typedef typename LK::Approximate_kernel AK;
typedef typename LK::Exact_kernel EK;
typedef typename LK::E2A E2A;
typedef typename Get_functor<AK, T>::type AC;
typedef typename Get_functor<EK, T>::type EC;
typedef typename map_result_tag<T>::type result_tag;
typedef typename Get_type<AK, result_tag>::type AT;
typedef typename Get_type<EK, result_tag>::type ET;
typedef typename Get_type<LK, result_tag>::type result_type;
// same as Handle = Lazy< AT, ET, E2A>
typedef typename Get_type<LK, FT_tag>::type FT;
Lazy_construction2(){}
Lazy_construction2(LK const&k):ac(k.approximate_kernel()),ec(k.exact_kernel()){}
CGAL_NO_UNIQUE_ADDRESS AC ac;
CGAL_NO_UNIQUE_ADDRESS EC ec;
template<class...L>
std::enable_if_t<(sizeof...(L)>0&&Constructible_from_each<FT,L...>::value), result_type> operator()(L const&...l) const {
CGAL_BRANCH_PROFILER(std::string(" failures/calls to : ") + std::string(CGAL_PRETTY_FUNCTION), tmp);
Protect_FPU_rounding<Protection> P;
try {
return new Lazy_rep_n<AT, ET, AC, EC, E2A, L...>(ac, ec, l...);
} catch (Uncertain_conversion_exception&) {
CGAL_BRANCH_PROFILER_BRANCH(tmp);
Protect_FPU_rounding<!Protection> P2(CGAL_FE_TONEAREST);
return new Lazy_rep_0<AT,ET,E2A>(ec(CGAL::exact(l)...));
}
}
template<class Iter>
std::enable_if_t<is_iterator<Iter>::value,result_type> operator()(int d, Iter const& f, Iter const& e) const {
typedef typename std::iterator_traits<Iter>::value_type TT;
return new Lazy_rep_XXX<AT, ET, AC, EC, E2A, TT>(ac, ec, d, f, e);
}
template<class Iter, class L>
std::enable_if_t<is_iterator<Iter>::value,result_type> operator()(Iter const& f, Iter const& e, L const&l) const {
typedef typename std::iterator_traits<Iter>::value_type TT;
return new Lazy_rep_YYY<AT, ET, AC, EC, E2A, TT, L>(ac, ec, f, e, l);
}
template<class Iter>
std::enable_if_t<is_iterator<Iter>::value,result_type> operator()(Iter const& f, Iter const& e) const {
return operator()(std::distance(f, e), f, e);
}
// FIXME: this forces us to have default constructors for all types, try to make its instantiation lazier
result_type operator()() const
{
return new Lazy_rep_0<AT,ET,E2A>();
}
};
#endif
template <class EK_, class AK_, class E2A_, class Kernel_>
struct Lazy_cartesian_types

View File

@ -367,20 +367,6 @@ void test2(){
assert(abs(cent1[0]-2)<.0001);
assert(abs(cent1[1]+3)<.0001);
assert(abs(sp.squared_radius()-25)<.0001);
#if 1
// Fails for an exact kernel
typedef typename K1::Point_of_sphere_d PS;
PS ps Kinit(point_of_sphere_d_object);
P psp0=ps(sp,0);
P psp1=ps(sp,1);
P psp2=ps(sp,2);
assert(!ed(psp0,psp1));
assert(!ed(psp0,psp2));
assert(!ed(psp2,psp1));
assert(abs(sd(cent0,psp0)-25)<.0001);
assert(abs(sd(cent0,psp1)-25)<.0001);
assert(abs(sd(cent0,psp2)-25)<.0001);
#endif
P x2py1 = tp(x2,y1);
assert(x2py1[1]==-2);
WP tw[]={cwp(cp(5,0),1.5),cwp(cp(2,std::sqrt(3)),1),cwp(cp(2,-std::sqrt(3)),1)};
@ -416,6 +402,46 @@ void test2(){
D un10; CGAL_USE(un10);
}
// Fails for an exact kernel, so I split it here
template<class Ker>
void test2i(){
typedef Ker K1;
typedef typename K1::Point_d P;
typedef typename K1::Sphere_d Sp;
typedef typename K1::Point_of_sphere_d PS;
typedef typename K1::Construct_point_d CP;
typedef typename K1::Construct_sphere_d CSp;
typedef typename K1::Equal_d E;
typedef typename K1::Squared_distance_d SD;
typedef typename K1::Center_of_sphere_d COS;
Ker k
#if 0
(2)
#endif
;
CP cp Kinit(construct_point_d_object);
PS ps Kinit(point_of_sphere_d_object);
CSp csp Kinit(construct_sphere_d_object);
E ed Kinit(equal_d_object);
SD sd Kinit(squared_distance_d_object);
COS cos Kinit(center_of_sphere_d_object);
P z0=cp( 0+2,5-3);
P z1=cp(-5+2,0-3);
P z2=cp( 3+2,4-3);
P tabz[]={z0,z1,z2};
Sp sp = csp(tabz+0,tabz+3);
P cent0=cos(sp);
P psp0=ps(sp,0);
P psp1=ps(sp,1);
P psp2=ps(sp,2);
assert(!ed(psp0,psp1));
assert(!ed(psp0,psp2));
assert(!ed(psp2,psp1));
assert(abs(sd(cent0,psp0)-25)<.0001);
assert(abs(sd(cent0,psp1)-25)<.0001);
assert(abs(sd(cent0,psp2)-25)<.0001);
}
#if defined(BOOST_MSVC)
# pragma warning(push)
# pragma warning(disable: 4512)
@ -426,15 +452,12 @@ template<class CP> struct Construct_point3_helper {
Construct_point3_helper(CP const& x) : cp(x) {}
template<class T1,class T2,class T3>
typename CP::result_type operator()(T1 const&t1, T2 const&t2, T3 const&t3)const{
//double tab[]={(double)t1,(double)t2,(double)t3};
// The lazy kernel stores iterators, not a vector<double>, so the array must stay alive until update_exact()! For the tests I am keeping the memory leak for now, it is more convenient.
double*tab=new double[3]{(double)t1,(double)t2,(double)t3};
double tab[]={(double)t1,(double)t2,(double)t3};
return cp(tab+0,tab+3);
}
template<class T1,class T2,class T3,class T4>
typename CP::result_type operator()(T1 const&t1, T2 const&t2, T3 const&t3, T4 const&t4)const{
// Same discussion as above
double*tab=new double[3]{(double)t1,(double)t2,(double)t3};
double tab[]={(double)t1,(double)t2,(double)t3};
return cp(tab+0,tab+3,t4);
}
};
@ -693,12 +716,12 @@ CGAL_static_assertion((boost::is_same<CGAL::Dimension_tag<3>,CGAL::Ambient_dimen
int main(){
//Broken with Linear_base_d (output iterator)
//test2<CGAL::Kernel_d_interface<KK> >();
test2<Ker2>();
test2<Ker2>(); test2i<Ker2>();
test3<Ker3>();
test3<Kerd>();
//test2<CGAL::Epeck_d<CGAL::Dimension_tag<2>>>();
//test3<CGAL::Epeck_d<CGAL::Dimension_tag<3>>>();
//test3<CGAL::Epeck_d<CGAL::Dynamic_dimension_tag>>();
test2<CGAL::Epeck_d<CGAL::Dimension_tag<2>>>();
test3<CGAL::Epeck_d<CGAL::Dimension_tag<3>>>();
test3<CGAL::Epeck_d<CGAL::Dynamic_dimension_tag>>();
}
#endif