/*============================================================================= This file is part of FLINT. FLINT is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. FLINT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with FLINT; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =============================================================================*/ /****************************************************************************** Copyright (C) 2013 Tom Bachmann ******************************************************************************/ // 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