/*
Copyright (C) 2013 Tom Bachmann
This file is part of FLINT.
FLINT is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License (LGPL) as published
by the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version. See .
*/
// This file contains helpers for evaluating expression templates.
#ifndef CXX_EVALUATION_TOOLS_H
#define CXX_EVALUATION_TOOLS_H
#include
#include "../flint.h" // FLINT_MAX and FLINT_MIN
#include "expression_traits.h"
#include "mp.h"
#include "rules.h"
#include "tuple.h"
namespace flint {
namespace mp {
// Find the highest-priority implemented evaluation rule, if any.
// TODO move to tools?
template
struct find_evaluation
{
private:
typedef rules::evaluation r2;
typedef rules::evaluation r1;
typedef rules::evaluation r0;
typedef traits::is_implemented i2;
typedef traits::is_implemented i1;
typedef traits::is_implemented i0;
public:
typedef typename mp::select, r2,
mp::and_v, r1,
mp::and_v, r0
>::type type;
};
} // mp
namespace tools {
namespace tdetail {
template
struct cmp_invert
{
static int get(const T& t, const U& u)
{
return -rules::cmp::get(u, t);
}
};
}
// A version of the cmp rule which tries both argument orders
template
struct symmetric_cmp
: mp::if_ >,
rules::cmp,
typename mp::if_ >,
tdetail::cmp_invert,
rules::UNIMPLEMENTED
>::type
>::type { };
// A version of equals which uses cmp if possible
namespace tdetail {
template
struct equals_using_cmp_ : rules::UNIMPLEMENTED { };
template
struct equals_using_cmp_ > >::type>
{
static bool get(const T& t, const U& u)
{
return tools::symmetric_cmp::get(t, u) == 0;
}
};
} // tdetail
template
struct equals_using_cmp
: mp::if_ >,
rules::equals, tdetail::equals_using_cmp_ >::type { };
// Automatic printing if to_string is implemented
namespace tdetail {
template
struct print_using_str_ : rules::UNIMPLEMENTED { };
template
struct print_using_str_ > >::type>
{
static void doit(const T& v, std::ostream& o)
{
int base = 10;
std::ios_base::fmtflags ff = o.flags();
if(ff & o.hex)
base = 16;
if(ff & o.oct)
base = 8;
o << v.to_string(base);
}
};
} // tdetail
template
struct print_using_str
: mp::if_ >,
rules::print, tdetail::print_using_str_ >::type { };
// Finding a subexpression of precsribed type
namespace tdetail {
template
struct find_subexpr_helper2;
template
struct find_subexpr_helper
{
typedef find_subexpr_helper2 fsh;
typedef typename fsh::rtype rtype;
static const bool val = fsh::val;
static rtype get(const Expr& e) {return fsh::get(e._data());}
};
template
struct find_subexpr_helper >::type>
{
static const bool val = true;
typedef const Expr& rtype;
static rtype get(rtype t) {return t;}
};
template
struct find_subexpr_helper,
mp::not_ > > >::type>
{
static const bool val = false;
typedef void rtype;
};
template
struct find_subexpr_helper2
{
typedef find_subexpr_helper2 fsh;
typedef typename fsh::rtype rtype;
static const bool val = fsh::val;
static rtype get(const Data& d) {return fsh::get(d.tail);}
};
template
struct find_subexpr_helper2,
typename mp::enable_if::type> >::type>
{
static const bool val = true;
typedef typename traits::basetype::type head_t;
typedef find_subexpr_helper fsh;
typedef typename fsh::rtype rtype;
static rtype get(const tuple& d) {return fsh::get(d.head);}
};
template
struct find_subexpr_helper2
{
static const bool val = false;
typedef void rtype;
};
} // tdetail
// A predicate which applies if the argument type equals T.
template
struct equal_types_pred
{
template struct type : mp::equal_types { };
};
// Given an expression template Expr, traverse the tree of data arguments
// until an argument matching the predicate Pred is found. Here pred must have
// a member template "type" performing the boolean computation.
// See equal_types_pred for an example.
// If there is no matching subexpression, a compile time error will be
// encountered.
// The current implementation performs depth-first search.
template
inline typename tdetail::find_subexpr_helper::rtype
find_subexpr(const Expr& e)
{
return tdetail::find_subexpr_helper::get(e);
}
// Find a subexpression of type T.
template
inline const T& find_subexpr_T(const Expr& e)
{
return find_subexpr >(e);
}
// Boolean computation to determine if find_subexpr above will work.
template
struct has_subexpr
: tdetail::find_subexpr_helper { };
// A helper to invoke htuples::fill with instantiate_temporaries
namespace tdetail {
template
struct fill_tmps_helper
{
const Expr& expr;
fill_tmps_helper(const Expr& e) : expr(e) {};
template
T create() const {return rules::instantiate_temporaries::get(expr);}
};
} // tdetail
template
tdetail::fill_tmps_helper temporaries_filler(const Expr& e)
{
return tdetail::fill_tmps_helper(e);
}
// A helper to "evaluate" a single term, independend of whether or not it is
// actually an expression template
template
struct evaluation_helper
{
typedef typename traits::basetype::type type;
typedef typename traits::forwarding::type ftype;
typedef ftype etype;
static ftype get(const type& t) {return t;}
typedef empty_tuple temporaries_t;
};
template
struct evaluation_helper >::type>
{
typedef typename T::evaluated_t type;
typedef const typename T::evaluated_t& ftype;
typedef type etype;
static type get(const T& t) {return t.evaluate();}
typedef typename T::ev_traits_t::temp_rule_t::temporaries_t temporaries_t;
};
///////////////////////////////////////////////////////////////////////////
// Helper to evaluate n terms
///////////////////////////////////////////////////////////////////////////
// The template argument is an arbitrary argument tuple
template
struct evaluate_n;
// Count the number of non-immediate terms in arguments
template
struct count_nonimm
{
static const unsigned val =
traits::is_lazy_expr::val
+ count_nonimm::val;
};
template<>
struct count_nonimm
{
static const unsigned val = 0;
};
template
struct evaluated_args_tuple
{
typedef typename tools::evaluation_helper::ftype evt;
typedef typename evaluated_args_tuple::type tail_t;
typedef tuple type;
};
template<>
struct evaluated_args_tuple
{
typedef empty_tuple type;
};
namespace tdetail {
// Unoptimized evaluation (in order)
// The constructor sets up any local temporaries (coming from non-merging).
// Then init does the actual computation. Note that init chains at the end
// (in contrast to construction), so we do evaluate in order.
template
struct evaluate_n_unopt
{
// case where head is immediate
typedef evaluate_n_unopt next_t;
typedef typename next_t::temporaries_t temporaries_t;
typedef typename Args::head_t headr_t;
next_t next;
headr_t res;
headr_t gethead() const
{
return res;
}
void init(const Args& args, temporaries_t temps)
{
next.init(args.tail, temps);
}
evaluate_n_unopt(const Args& args)
: next(args.tail), res(args.head) {}
};
template<>
struct evaluate_n_unopt
{
// basecase
typedef empty_tuple temporaries_t;
void init(empty_tuple, empty_tuple) {}
evaluate_n_unopt(empty_tuple) {}
};
template
struct evaluate_n_unopt,
mp::not_ > > >::type>
{
// Case with non-merging lazy head
typedef evaluate_n_unopt next_t;
typedef typename Args::head_t expr_t;
typedef typename expr_t::ev_traits_t::temp_rule_t rule_t;
typedef typename rule_t::return_t tmp_t;
typedef mp::merge_tuple merger;
typedef typename merger::type temporaries_t;
typedef typename traits::forwarding::type headr_t;
next_t next;
tmp_t tmp;
headr_t gethead() const
{
return tmp;
}
evaluate_n_unopt(const Args& args)
: next(args.tail), tmp(
rules::instantiate_temporaries::get(args.head))
{}
void init(const Args& args, temporaries_t temps)
{
rule_t::doit(args.head._data(), merger::get_first(temps), &tmp);
next.init(args.tail, merger::get_second(temps));
}
};
template
struct evaluate_n_unopt,
traits::use_temporary_merging<
typename Args::head_t::evaluated_t> > >::type>
{
// Case with merging lazy head
typedef evaluate_n_unopt next_t;
typedef typename Args::head_t expr_t;
typedef typename expr_t::ev_traits_t::temp_rule_t rule_t;
typedef typename rule_t::return_t tmp_t;
typedef mp::merge_tuple > merger;
typedef typename merger::type temporaries_t;
typedef typename traits::forwarding::type headr_t;
next_t next;
tmp_t* tmp;
headr_t gethead() const
{
return *tmp;
}
evaluate_n_unopt(const Args& args)
: next(args.tail) {}
void init(const Args& args, temporaries_t temps)
{
tmp = merger::get_second(temps).head;
rule_t::doit(args.head._data(), merger::get_first(temps), tmp);
next.init(args.tail, merger::get_second(temps).tail);
}
};
template
struct unopt_get
{
typedef unopt_get getn;
typedef typename getn::type type;
static type get(const evaluate_n_unopt& e) {return getn::get(e.next);}
};
template
struct unopt_get
{
typedef evaluate_n_unopt evalt;
typedef typename evalt::headr_t type;
static type get(const evalt& e) {return e.gethead();}
};
template
struct unopt_gettuple
{
typedef unopt_gettuple next;
typedef evaluate_n_unopt eval_t;
typedef tuple type;
static type get(const eval_t& e)
{
return type(e.gethead(), next::get(e.next));
}
};
template<>
struct unopt_gettuple
{
typedef empty_tuple type;
template
static type get(const T&) {return empty_tuple();}
};
// Optimized case with precisely two non-immediates
template
struct evaluate_n_2_analyze
{
typedef evaluate_n_2_analyze next;
static const unsigned first = next::first + 1;
static const unsigned second = next::second + 1;
};
template
struct evaluate_n_2_analyze >::type>
{
typedef evaluate_n_2_analyze next;
static const unsigned first = 0;
static const unsigned second = next::second + 1;
};
template
struct evaluate_n_2_analyze >::type>
{
static const unsigned first = 0;
static const unsigned second = 0;
};
template
struct evaluate_2;
// Case where neither is immediate, no merging
template
struct evaluate_2,
traits::is_lazy_expr,
mp::not_ >,
mp::not_ >
> >::type>
{
private:
typedef typename Expr1::ev_traits_t::temp_rule_t rule1_t;
typedef typename Expr2::ev_traits_t::temp_rule_t rule2_t;
public:
typedef typename rule1_t::return_t return1_t;
typedef typename rule2_t::return_t return2_t;
private:
typedef typename rule1_t::temporaries_t temporaries1_t;
typedef typename rule2_t::temporaries_t temporaries2_t;
typedef mp::merge_tuple merger;
return1_t tmp1;
return2_t tmp2;
public:
typedef typename merger::type temporaries_t;
evaluate_2(temporaries_t temps, const Expr1& e1, const Expr2& e2)
: tmp1(rules::instantiate_temporaries::get(e1)),
tmp2(rules::instantiate_temporaries::get(e2))
{
rule1_t::doit(e1._data(), merger::get_first(temps), &tmp1);
rule2_t::doit(e2._data(), merger::get_second(temps), &tmp2);
}
const return1_t& get1() const {return tmp1;}
const return2_t& get2() const {return tmp2;}
};
// Case where neither is immediate, first has merging, second does not
template
struct evaluate_2,
traits::is_lazy_expr,
traits::use_temporary_merging,
mp::not_ >
> >::type>
{
private:
typedef typename Expr1::ev_traits_t::temp_rule_t rule1_t;
typedef typename Expr2::ev_traits_t::temp_rule_t rule2_t;
public:
typedef typename rule1_t::return_t return1_t;
typedef typename rule2_t::return_t return2_t;
private:
typedef typename rule1_t::temporaries_t temporaries1_t;
typedef typename rule2_t::temporaries_t temporaries2_t;
typedef mp::merge_tuple::type,
temporaries1_t> merger1;
typedef mp::merge_tuple merger2;
return2_t tmp2;
return1_t* ret1;
public:
typedef typename merger2::type temporaries_t;
evaluate_2(temporaries_t temps, const Expr1& e1, const Expr2& e2)
: tmp2(rules::instantiate_temporaries::get(e2))
{
rule2_t::doit(e2._data(), merger2::get_second(temps), &tmp2);
ret1 = merger1::get_first(merger2::get_first(temps)).head;
rule1_t::doit(e1._data(),
merger1::get_second(merger2::get_first(temps)), ret1);
}
const return1_t& get1() const {return *ret1;}
const return2_t& get2() const {return tmp2;}
};
// Case where neither is immediate, second has merging, first does not
template
struct evaluate_2,
traits::is_lazy_expr,
traits::use_temporary_merging,
mp::not_ >
> >::type>
{
// XXX this is copy-paste from above case where right is immediate
private:
typedef evaluate_2 ev2_t;
ev2_t ev2;
public:
typedef typename ev2_t::return1_t return2_t;
typedef typename ev2_t::return2_t return1_t;
typedef typename ev2_t::temporaries_t temporaries_t;
evaluate_2(temporaries_t temps, const Expr1& e1, const Expr2& e2)
: ev2(temps, e2, e1) {};
const return1_t& get1() const {return ev2.get2();}
const return2_t& get2() const {return ev2.get1();}
};
// Case where neither is immediate, all merging
template
struct evaluate_2,
traits::is_lazy_expr,
traits::use_temporary_merging,
traits::use_temporary_merging > >::type>
{
private:
typedef typename Expr1::ev_traits_t::temp_rule_t rule1_t;
typedef typename Expr2::ev_traits_t::temp_rule_t rule2_t;
public:
typedef typename rule1_t::return_t return1_t;
typedef typename rule2_t::return_t return2_t;
private:
typedef typename rule1_t::temporaries_t temporaries1_t;
typedef typename rule2_t::temporaries_t temporaries2_t;
template
friend struct evaluate_2;
// We can either evaluate the Expr1 first and then Expr2, or the other
// way round. We would like to choose the most efficient strategy.
// Since we have no access to other metrics, we compare the number of
// temporaries required (see typedef of doit below).
struct doit_1
{
typedef mp::merge_tuple::type,
temporaries2_t> merger2;
typedef mp::merge_tuple,
temporaries1_t> merger1;
typedef typename merger1::type temporaries_t;
static void init(temporaries_t temps, const Expr1& e1, const Expr2& e2,
return1_t*& ret1, return2_t*& ret2)
{
temporaries1_t temps1 = merger1::get_second(temps);
temporaries2_t temps2 =
merger2::get_second(merger1::get_first(temps).tail);
ret1 = merger1::get_first(temps).head;
ret2 =
merger2::get_first(merger1::get_first(temps).tail).head;
rule1_t::doit(e1._data(), temps1, ret1);
rule2_t::doit(e2._data(), temps2, ret2);
}
};
struct doit_2
{
typedef typename evaluate_2::doit_1 doit_other;
typedef typename doit_other::temporaries_t temporaries_t;
static void init(temporaries_t temps, const Expr1& e1, const Expr2& e2,
return1_t*& ret1, return2_t*& ret2)
{
doit_other::init(temps, e2, e1, ret2, ret1);
}
};
typedef typename mp::if_v<
(doit_1::temporaries_t::len <= doit_2::temporaries_t::len),
doit_1, doit_2>::type doit;
return1_t* ret1;
return2_t* ret2;
public:
typedef typename doit::temporaries_t temporaries_t;
evaluate_2(temporaries_t temps, const Expr1& e1, const Expr2& e2)
{
doit::init(temps, e1, e2, ret1, ret2);
}
const return1_t& get1() const {return *ret1;}
const return2_t& get2() const {return *ret2;}
};
template
struct evaluate_n_2_get
{
template
static typename mp::tuple_get::type get(const Args& args,
const First&, const Second&)
{
return mp::tuple_get::get(args);
}
};
template
struct evaluate_n_2_get
{
template
static const First& get(const Args&, const First& f, const Second&)
{
return f;
}
};
template
struct evaluate_n_2_get
{
template
static const Second& get(const Args&, const First&, const Second& s)
{
return s;
}
};
template
struct evaluate_n_2_gettuple
{
template
static Tuple get(const T& t)
{
return Tuple(t.template get(),
evaluate_n_2_gettuple::get(t));
}
};
template
struct evaluate_n_2_gettuple
{
template
static empty_tuple get(const T&) {return empty_tuple();}
};
} // tdetail
template
struct evaluate_n
{
typedef tdetail::evaluate_n_unopt eval_t;
typedef typename eval_t::temporaries_t temporaries_t;
typedef typename tdetail::unopt_gettuple::type evtup_t;
eval_t eval;
evaluate_n(const Args& args, temporaries_t temps)
: eval(args)
{
eval.init(args, temps);
}
template
typename tdetail::unopt_get::type get() const
{
return tdetail::unopt_get::get(eval);
}
evtup_t gettuple() const
{
return tdetail::unopt_gettuple::get(eval);
}
};
template
struct evaluate_n::val == 2>::type>
{
typedef tdetail::evaluate_n_2_analyze analysis;
static const unsigned first = analysis::first;
static const unsigned second = analysis::second;
typedef mp::tuple_get getfirst;
typedef mp::tuple_get getsecond;
typedef typename getfirst::type first_t;
typedef typename getsecond::type second_t;
typedef tdetail::evaluate_2 ev_t;
typedef typename ev_t::temporaries_t temporaries_t;
const Args& args;
ev_t ev;
evaluate_n(const Args& a, temporaries_t temps)
: args(a), ev(temps, getfirst::get(a), getsecond::get(a)) {}
typedef typename evaluated_args_tuple::type evtup_t;
template
typename mp::tuple_get::type get() const
{
return tdetail::evaluate_n_2_get::get(
args, ev.get1(), ev.get2());
}
evtup_t gettuple() const
{
return tdetail::evaluate_n_2_gettuple::get(*this);
}
};
///////////////////////////////////////////////////////////////////////////
// Helper to evaluate three homogeneous terms
///////////////////////////////////////////////////////////////////////////
//
// Evaluation using ternary operators is actually surprisingly hard.
// Consider e.g. a + b*c. The number of temporaries needed for this depends
// on whether or not b, c are immediates, and on the numbers of temporaries
// needed for each non-immediate expression.
namespace tdetail {
// This struct deals with the difficulties in whether b or c might be
// immediate.
template
struct ternary_hhelper;
// To be specialised below.
} // tdetail
// The following struct can be used to simplify writing evaluation rules which
// use ternary operations (addmul, submul).
//
// In the situation of a + b*c, the optimization can be applied if
// - the result goes to a temporary (i.e. we can write to it prematurely)
// - a is not an immediate
// - a, b, c are of the same type, and addmul is available for this type
// If so, one needs to evaluate a into the return location and b, c into
// temporaries; after that addmul can be applied.
//
// The ternary_helper facilitates both the checking if we are in the right
// situation and the intermediate evaluations. Instantiate it with
// "T" being your ground type (for which addmul is implemented), "Left" the type
// of a, "Right1" the type of b and "Right2" the type of c.
// Then the member enable::type can be used in SFINAE situations to
// conditionally enable a template only if we are in the addmul situation.
// The member type "temporaries_t" and static member function "doit" can be used
// to evaluate the intermediate terms.
//
// It may sometimes be useful to preclude a certain type of expression for a.
// (E.g. one needs rules for both a + b*c and b*c + a, but then which of these
// applies to b*c + a*d?) To do this, pass the operation you want to exclude in
// "disable_op".
//
// NOTE: in the current implementation, ternary_helper only works with
// *homogeneous* expressions. These are defined to be expressions evaluating to
// type T, which only need temporaries of type T.
// This condition is included in the checks done by the enable member type.
// NOTE: This implementation does not honor use_temporary_merging!
//
template
struct ternary_helper { };
template
struct ternary_helper,
traits::is_expression::type>,
traits::is_expression::type> > >::type>
{
typedef typename traits::basetype::type right1_t;
typedef typename traits::basetype::type right2_t;
typedef typename Left::ev_traits_t::temp_rule_t evl;
typedef tools::evaluation_helper evhr1;
typedef tools::evaluation_helper evhr2;
typedef mp::enable_if,
traits::is_homogeneous_tuple,
traits::is_homogeneous_tuple,
traits::is_homogeneous_tuple<
typename mp::make_tuple<
typename evl::return_t,
typename evhr1::type,
typename evhr2::type>::type, T>,
mp::not_ >
> > enable;
typedef tdetail::ternary_hhelper::val,
traits::is_immediate::val> inner;
typedef typename inner::temporaries_t temporaries_t;
// evaluate left into res, rigth1 and right2 to arbitrary location,
// set toright1, toright2 to these locations
static void doit(const Left& left, const right1_t& right1,
const right2_t& right2, temporaries_t temps, T* res,
const T*& toright1, const T*& toright2)
{
inner::doit(left, right1, right2, temps, res, toright1, toright2);
}
};
namespace tdetail {
// Case where both are immediate.
template
struct ternary_hhelper
{
typedef typename Left::ev_traits_t::temp_rule_t evl;
static const unsigned norig = evl::temporaries_t::len;
static const unsigned ntemps = FLINT_MAX(norig, 1);
typedef typename mp::make_homogeneous_tuple::type
temporaries_t;
static void doit(const Left& left, const right1_t& right1,
const right2_t& right2, temporaries_t temps, T* res,
const T*& toright1, const T*& toright2)
{
evl::doit(left._data(), mp::htuples::extract(temps), res);
toright1 = &right1;
toright2 = &right2;
}
};
// If c is immediate but b is not, there are still two subcases.
// Let t1 be the number of temporaries needed to evaluate a, and
// t2 the number for b. If t1 >= t2, then we need to evaluate a first.
// Otherwise b.
// In any case, the number of temporaries is at least two (for the two return
// values), and generically equal to the maximum of t1 and t2. If however
// t1 == t2, then we need an additional temporary.
template
struct ternary_hhelper_1imm;
// Case where t1 >= t2
template
struct ternary_hhelper_1imm
{
typedef typename Left::ev_traits_t::temp_rule_t evl;
typedef typename right1_t::ev_traits_t::temp_rule_t evr;
static const unsigned t1 = evl::temporaries_t::len;
static const unsigned t2 = evr::temporaries_t::len;
// t1 >= t2
template
static void doit(const Left& left, const right1_t& right1,
Temps temps, T* res,
const T*& toright1)
{
evl::doit(left._data(), mp::htuples::extract(temps), res);
typename Temps::tail_t nores = mp::htuples::removeres(temps, res);
evr::doit(right1._data(),
mp::htuples::extract(nores), nores.head);
toright1 = nores.head;
}
};
// Case where t1 < t2
template
struct ternary_hhelper_1imm
{
typedef typename Left::ev_traits_t::temp_rule_t evl;
typedef typename right1_t::ev_traits_t::temp_rule_t evr;
static const unsigned t1 = evl::temporaries_t::len;
static const unsigned t2 = evr::temporaries_t::len;
// t1 < t2
template
static void doit(const Left& left, const right1_t& right1,
Temps temps, T* res,
const T*& toright1)
{
typedef typename Temps::tail_t tail_t;
tail_t nores = mp::htuples::removeres(temps, res);
evr::doit(right1._data(),
mp::htuples::extract(temps), nores.head);
toright1 = nores.head;
evl::doit(left._data(),
mp::htuples::extract(tail_t(res, nores.tail)), res);
}
};
// Case where c is immediate.
template
struct ternary_hhelper
{
typedef typename Left::ev_traits_t::temp_rule_t evl;
typedef tools::evaluation_helper evhr1;
static const unsigned t1 = evl::temporaries_t::len;
static const unsigned t2 = evhr1::temporaries_t::len;
static const unsigned ntemps = FLINT_MAX(2, FLINT_MAX(t1, t2) + (t1 == t2));
typedef ternary_hhelper_1imm= t2> thh1;
typedef typename mp::make_homogeneous_tuple::type
temporaries_t;
static void doit(const Left& left, const right1_t& right1,
const right2_t& right2, temporaries_t temps, T* res,
const T*& toright1, const T*& toright2)
{
toright2 = &right2;
thh1::doit(left, right1, temps, res, toright1);
}
};
// Case where b is immediate.
template
struct ternary_hhelper
{
typedef ternary_hhelper thh;
typedef typename thh::temporaries_t temporaries_t;
static void doit(const Left& left, const right1_t& right1,
const right2_t& right2, temporaries_t temps, T* res,
const T*& toright1, const T*& toright2)
{
thh::doit(left, right2, right1, temps, res, toright2, toright1);
}
};
// Case where neither is immediate.
template
struct ternary_hhelper
{
typedef typename Left::ev_traits_t::temp_rule_t evl;
typedef typename right1_t::ev_traits_t::temp_rule_t evr1;
typedef typename right2_t::ev_traits_t::temp_rule_t evr2;
static const unsigned t1 = evl::temporaries_t::len;
static const unsigned t2 = evr1::temporaries_t::len;
static const unsigned t3 = evr2::temporaries_t::len;
// m1, m2, m3 is t1, t2, t3 reordered s.t. m1 >= m2 >= m3
static const unsigned m1 = FLINT_MAX(t1, FLINT_MAX(t2, t3));
static const unsigned m3 = FLINT_MIN(t1, FLINT_MIN(t2, t3));
static const unsigned m2 = t1 + t2 + t3 - m1 - m3;
// The following is obtained by case analysis
static const unsigned ntemps =
(t1 == t2 && t2 == t3) ? FLINT_MAX(3, t1+2) : // all equal
((m1 > m2 && m2 > m3) ? FLINT_MAX(3, m1) : // all distinct
(m1 == m2 ? FLINT_MAX(m1+1, 3) // first two equal
: FLINT_MAX(m1, FLINT_MAX(m2+2, 3)))); // second two equal
typedef typename mp::make_homogeneous_tuple::type
temporaries_t;
struct resaccess
{
T* res;
resaccess(T* r) : res(r) {};
template
typename Temps::tail_t doit(const Data& d, Temps temps)
{
Eval::doit(d,
mp::htuples::extract(temps),
res);
return mp::htuples::extract(temps);
}
};
struct toaccess
{
const T*& right;
toaccess(const T*& r) : right(r) {};
template
typename Temps::tail_t doit(const Data& d, Temps temps)
{
Eval::doit(d,
mp::htuples::extract(temps),
temps.head);
right = temps.head;
return temps.tail;
}
};
struct doit_really
{
template
static void doit(const E1& e1, const E2& e2,
const E3& e3, temporaries_t temps,
A1 a1, A2 a2, A3 a3)
{
typedef typename E1::ev_traits_t::temp_rule_t ev1;
typedef typename E2::ev_traits_t::temp_rule_t ev2;
typedef typename E3::ev_traits_t::temp_rule_t ev3;
a3.template doit(e3._data(),
a2.template doit(e2._data(),
a1.template doit(e1._data(), temps)));
}
};
struct dont_doit
{
template
static void doit(const E1& e1, const E2& e2,
const E3& e3, temporaries_t temps,
A1 a1, A2 a2, A3 a3)
{
}
};
template
static void doit_sort(const E1& e1, const E2& e2,
const E3& e3, temporaries_t temps,
A1 a1, A2 a2, A3 a3)
{
typedef typename E1::ev_traits_t::temp_rule_t ev1;
typedef typename E2::ev_traits_t::temp_rule_t ev2;
typedef typename E3::ev_traits_t::temp_rule_t ev3;
static const unsigned u1 = ev1::temporaries_t::len;
static const unsigned u2 = ev2::temporaries_t::len;
static const unsigned u3 = ev3::temporaries_t::len;
if(u1 < u2)
return doit_sort(e2, e1, e3, temps, a2, a1, a3);
if(u2 < u3)
return doit_sort(e1, e3, e2, temps, a1, a3, a2);
// If we reach this point, u1 >= u2 >= u3.
// However, even if this is not the case, the following line (and
// everything it instantiates) still has to compile.
mp::if_v<(u1 >= u2 && u2 >= u3), doit_really, dont_doit>::type::doit(
e1, e2, e3, temps, a1, a2, a3);
}
static void doit(const Left& left, const right1_t& right1,
const right2_t& right2, temporaries_t temps, T* res,
const T*& toright1, const T*& toright2)
{
// We re-order the temporaries in such a way that res is at the
// very end. When evaluating things in the correct order, it is then
// always correct to take temporaries from the front, and drop them
// from the front.
temporaries_t temps_reordered = mp::concat_tuple<
typename temporaries_t::tail_t,
typename mp::make_tuple::type>::doit(
mp::htuples::removeres(temps, res),
mp::make_tuple::make(res));
doit_sort(left, right1, right2, temps_reordered,
resaccess(res), toaccess(toright1), toaccess(toright2));
}
};
} // tdetail
// A helper condition for use with FLINT_DEFINE_*_COND?
template
struct is_bool : mp::equal_types { };
} // tools
} // flint
#endif