/* 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 . */ // Helpers to define concrete subclasses of expression. // Contrary to other parts of this library, they are tailored very // specifically towards FLINT. #include "../flint.h" #include "mp.h" #include "expression.h" #include "expression_traits.h" #include "evaluation_tools.h" #include "tuple.h" #ifndef CXX_FLINT_CLASSES_H #define CXX_FLINT_CLASSES_H // Flint classes distinguish themselves from "ordinary" expression template // classes by a public typedef IS_FLINT_CLASS (see also FLINTXX_DEFINE_BASICS // below). Most functionality in this header disables itself when used on a // non-flint class. // For all flint classes, Data of immediates must have typedefs data_ref_t and // data_srcref_t. // The immediates of any flint class come in three "flavours": ordinary, ref // and srcref. Most of the classes below are used to convert these flavours. // In order for this to work, the expression template class must contain a // public typedef c_base_t which is the underlying "basic" C type (so not the // length one array that is usually used), e.g. fmpz_poly_struct. Conversion to // reference type then yields expression templates which have Data set to // ref_data or srcref_data. These implementation work as // long as all data is stored in c_base_t. If not (e.g. for padic, where there // is an additional reference to the context), ref_data and srcref_data have to // be specialised appropriately. namespace flint { namespace flint_classes { template struct ref_data { typedef void IS_REF_OR_CREF; typedef Wrapped wrapped_t; typedef Inner* data_ref_t; typedef const Inner* data_srcref_t; Inner* inner; ref_data(Wrapped& o) : inner(o._data().inner) {} static ref_data make(Inner* f) {return ref_data(f);} private: ref_data(Inner* fp) : inner(fp) {} }; template struct srcref_data { typedef void IS_REF_OR_CREF; typedef Wrapped wrapped_t; typedef const Inner* data_ref_t; typedef const Inner* data_srcref_t; const Inner* inner; srcref_data(const Wrapped& o) : inner(o._data().inner) {} srcref_data(Ref o) : inner(o._data().inner) {} static srcref_data make(const Inner* f) {return srcref_data(f);} private: srcref_data(const Inner* fp) : inner(fp) {} }; // Helper to determine if T is a flint class. template struct is_flint_class : mp::false_ { }; template struct is_flint_class : mp::true_ { }; // From a lazy or immediate flint expression, obtain the evaluated // non-reference type. // Examples: fmpzxx -> fmpzxx // fmpzxx_ref -> fmpzxx // fmpzxx + fmpzxx -> fmpzxx template struct to_nonref {typedef typename T::evaluated_t type;}; template struct to_nonref { typedef typename T::data_t::wrapped_t type; }; template struct c_base_t { typedef typename T::c_base_t type; }; // Given a lazy or non-lazy flint expression, obtain th evaluated reference // type. // Examples: fmpzxx -> fmpzxx_ref // fmpzxx_ref -> fmpzxx_ref // fmpzxx + fmpzxx -> fmpzxx_ref template struct to_ref { typedef typename T::template make_helper::type, typename c_base_t::type> >::type type; }; // Similarly for srcref. template struct to_srcref { typedef typename T::template make_helper::type, typename to_ref::type, typename c_base_t::type> >::type type; }; // Compute if Ref if the reference type belonging to compare. // Examples: fmpzxx_ref, fmpzxx + fmpzxx -> true_ // fmpzxx_srcref, fmpzxx -> false_ template struct is_ref : mp::equal_types::type> { }; // Similarly for srcref. template struct is_srcref : mp::equal_types::type> { }; // Similarly for non-ref. template struct is_nonref : mp::equal_types::type > { }; // Flint classes allow implicit conversion only in very special situations. // This template determines when. Currently, it is used exclusively to allow // implicit conversion to reference types. template struct enableimplicit : mp::false_ { }; template struct enableimplicit, is_flint_class > >::type> : mp::and_< traits::is_immediate_expr, traits::is_immediate_expr, mp::or_< mp::and_, is_nonref >, mp::and_, is_nonref >, mp::and_, is_ref > > > { }; // Helper template which allows accessing data_(src)ref_t on immediates, // without causing a compiler error on non-immediates. // The main use for this are the _fmpz(), _fmpq() etc methods, which only // work on immediates (but are defined on all instances). template struct maybe_data_ref { typedef void data_ref_t; typedef void data_srcref_t; }; template struct maybe_data_ref >::type> { typedef typename Expr::data_t::data_ref_t data_ref_t; typedef typename Expr::data_t::data_srcref_t data_srcref_t; }; // If Base is a non-ref flint class, determine if T is a source operand // (i.e. non-ref, ref or srcref type belong to Base) // Examples: fmpzxx, fmpzxx_srcref -> true // fmpzxx, fmpzxx -> true // fmpzxx, fmpqxx -> false template struct is_source : mp::false_ { }; template struct is_source, is_flint_class > >::type> : mp::or_, is_ref, is_srcref > { }; // Same with target (i.e. disallow srcref). template struct is_target : mp::false_ { }; template struct is_target, is_flint_class > >::type> : mp::or_, is_ref > { }; // Predicate version of the above. Useful for FLINT_DEFINE_*_COND. // See FLINTXX_COND_S and FLINTXX_COND_T template struct is_source_base { template struct type : is_source { }; }; template struct is_target_base { template struct type : is_target { }; }; // Helper for implementing x += y*z etc template struct ternary_assign_helper { typedef typename mp::make_tuple::type tup_t; typedef tools::evaluate_n ev2_t; typedef typename ev2_t::temporaries_t temporaries_t; typedef mp::back_tuple back_t; typename back_t::type backing; ev2_t ev2; static temporaries_t backtemps(typename back_t::type& backing) { temporaries_t temps; back_t::init(temps, backing); return temps; } ternary_assign_helper(const tup_t& tup) : backing(mp::htuples::fill( tools::temporaries_filler( tup.first()+tup.second() /* XXX */))), ev2(tup, backtemps(backing)) {} const T& getleft() {return ev2.template get<0>();} const T& getright() {return ev2.template get<1>();} }; template struct enable_ternary_assign : mp::enable_if::type, T>, traits::is_T_expr::type, T> >, T&> { }; // convenience helper template struct is_Base : mp::or_< traits::is_T_expr, is_source > { }; } // flint_classes namespace traits { // Enable evaluation into reference types. See can_evaluate_into in // expression_traits.h. // XXX why do we need to disable the case where T, U are equal? // Is not more special? template struct can_evaluate_into, flint_classes::is_flint_class, mp::not_ > > >::type> : flint_classes::is_ref { }; } // traits namespace detail { template struct should_enable_extra_ternop : mp::false_ { }; template struct should_enable_extra_ternop >::type> : mp::equal_types::type>::type> { }; } // detail // We add additional overloads for when the LHS is a reference type. The // problem is that the standard overloads take LHS via reference, and rvalues // (such as coming from fmpz_polyxx::get_coeff()) // cannot bind to this. In this case instead objects should be taken by value. // However, this will make the overload ambiguous. Hence we take by const // reference and then make an additional copy. template inline typename mp::enable_if, Expr1>::type operator+=(const Expr1& e1, const Expr2& e2) { Expr1(e1).set(e1 + e2); return e1; } template inline typename mp::enable_if, Expr1>::type operator-=(const Expr1& e1, const Expr2& e2) { Expr1(e1).set(e1 - e2); return e1; } template inline typename mp::enable_if, Expr1>::type operator*=(const Expr1& e1, const Expr2& e2) { Expr1(e1).set(e1 * e2); return e1; } template inline typename mp::enable_if, Expr1>::type operator/=(const Expr1& e1, const Expr2& e2) { Expr1(e1).set(e1 / e2); return e1; } template inline typename mp::enable_if, Expr1>::type operator%=(const Expr1& e1, const Expr2& e2) { Expr1(e1).set(e1 % e2); return e1; } template inline typename mp::enable_if, Expr1>::type operator<<=(const Expr1& e1, const Expr2& e2) { Expr1(e1).set(e1 << e2); return e1; } template inline typename mp::enable_if, Expr1>::type operator>>=(const Expr1& e1, const Expr2& e2) { Expr1(e1).set(e1 >> e2); return e1; } } // flint // macros that help defining flint classes #define FLINTXX_DEFINE_BASICS_NOFLINTCLASS(name) \ public: \ typedef typename base_t::evaluated_t evaluated_t; \ \ template \ struct doimplicit \ : flint_classes::enableimplicit { }; \ \ template \ name& operator=(const T& t) \ { \ this->set(t); \ return *this; \ } \ \ protected: \ explicit name(const Data& d) : base_t(d) {} \ \ template \ friend class expression; // all flint classes should have this #define FLINTXX_DEFINE_BASICS(name) \ public: \ typedef void IS_FLINT_CLASS; \ FLINTXX_DEFINE_BASICS_NOFLINTCLASS(name) \ // all flint classes should have this #define FLINTXX_DEFINE_CTORS(name) \ public: \ name() : base_t() {} \ template \ explicit name(const T& t, \ typename mp::disable_if >::type* = 0) \ : base_t(t) {} \ template \ explicit name(T& t, \ typename mp::disable_if >::type* = 0) \ : base_t(t) {} \ template \ name(const T& t, \ typename mp::enable_if >::type* = 0) \ : base_t(t) {} \ template \ name(T& t, \ typename mp::enable_if >::type* = 0) \ : base_t(t) {} \ template \ name(const T& t, const U& u) : base_t(t, u) {} \ template \ name(T& t, const U& u) : base_t(t, u) {} \ template \ name(const T& t, const U& u, const V& v) : base_t(t, u, v) {} \ template \ name(T& t, const U& u, const V& v) : base_t(t, u, v) {} \ template \ name(const T& t, const U& u, const V& v, const W& w) \ : base_t(t, u, v, w) {} \ template \ name(T& t, const U& u, const V& v, const W& w) \ : base_t(t, u, v, w) {} // Enable the flint reference type scheme. This typedefs c_base_t to ctype, // and adds the data access wrapper (like _fmpz(), _fmpq()) called accessname. // It also provides reference constructors from C types. // All flint classes should have this. #define FLINTXX_DEFINE_C_REF(name, ctype, accessname) \ public: \ typedef ctype c_base_t; \ typedef flint_classes::maybe_data_ref wrapped_traits; \ typename wrapped_traits::data_ref_t accessname() \ { \ return this->_data().inner; \ } \ typename wrapped_traits::data_srcref_t accessname() const \ { \ return this->_data().inner; \ } \ \ /* These only make sense with the reference types */ \ template \ static name make(T& f) \ { \ return name(Data::make(f)); \ } \ template \ static name make(const T& f) \ { \ return name(Data::make(f)); \ } \ template \ static name make(T& f, const U& u) \ { \ return name(Data::make(f, u)); \ } \ template \ static name make(const T& f, const U& u) \ { \ return name(Data::make(f, u)); \ } \ template \ static name make(const T& f, const U& u, const V& v) \ { \ return name(Data::make(f, u, v)); \ } // Add a statically forwarded constructor called name. (Forwarded to data_t). #define FLINTXX_DEFINE_FORWARD_STATIC(name) \ template \ static typename base_t::derived_t name(const T& f) \ { \ return typename base_t::derived_t(Data::name(f)); \ } \ template \ static typename base_t::derived_t name(const T& f, const U& u) \ { \ return typename base_t::derived_t(Data::name(f, u)); \ } // Add a static randomisation function. // XXX this is not really useful because the arguments are often different. #define FLINTXX_DEFINE_RANDFUNC(CBase, name) \ static CBase##xx_expression name(frandxx& state, flint_bitcnt_t bits) \ { \ CBase##xx_expression res; \ CBase##_##name(res._data().inner, state._data(), bits); \ return res; \ } // Add a forwarded unary operation to the class. Suppose there is a unary // operation foo() which returns my_typeA, and takes an argument of type // my_typeB. Now on instances my_typeB, you want to write x.bar() for foo(x). // Then add FLINTXX_DEFINE_MEMBER_UNOP_RTYPE_(my_typeA, bar, foo) to my_typeB. // // XXX due to circular definition problems, this cannot use the usual // unary_op_helper type approach, and the unop must return the same type // of expression #define FLINTXX_DEFINE_MEMBER_UNOP_RTYPE_(rettype, name, funcname) \ FLINT_UNOP_BUILD_RETTYPE(funcname, rettype, typename base_t::derived_t) \ name() const \ { \ return flint::funcname(*this); \ } // Convenience version when name==funcname #define FLINTXX_DEFINE_MEMBER_UNOP_RTYPE(rettype, name) \ FLINTXX_DEFINE_MEMBER_UNOP_RTYPE_(rettype, name, name) // Convenience version when rettype==argtype #define FLINTXX_DEFINE_MEMBER_UNOP_(name, funcname) \ FLINTXX_DEFINE_MEMBER_UNOP_RTYPE_(typename base_t::derived_t, name, funcname) // Convenience version when rettype==argtype and name==funcname #define FLINTXX_DEFINE_MEMBER_UNOP(name) FLINTXX_DEFINE_MEMBER_UNOP_(name, name) // Add a forwarded binary operation. It is not necessary to specify the return // type. #define FLINTXX_DEFINE_MEMBER_BINOP_(name, funcname) \ template \ typename detail::binary_op_helper::enable::type \ name(const T& t) const \ { \ return flint::funcname(*this, t); \ } // Convenience version when funcname==name. #define FLINTXX_DEFINE_MEMBER_BINOP(name) \ FLINTXX_DEFINE_MEMBER_BINOP_(name, name) #define FLINTXX_DEFINE_MEMBER_3OP_(name, funcname) \ template \ typename detail::nary_op_helper2::enable::type \ name(const T& t, const U& u) const \ { \ return flint::funcname(*this, t, u); \ } #define FLINTXX_DEFINE_MEMBER_3OP(name) \ FLINTXX_DEFINE_MEMBER_3OP_(name, name) #define FLINTXX_DEFINE_MEMBER_4OP_(name, funcname) \ template \ typename detail::nary_op_helper2::enable::type \ name(const T& t, const U& u, const V& v) const \ { \ return flint::funcname(*this, t, u, v); \ } #define FLINTXX_DEFINE_MEMBER_4OP(name) \ FLINTXX_DEFINE_MEMBER_4OP_(name, name) #define FLINTXX_DEFINE_MEMBER_5OP_(name, funcname) \ template \ typename detail::nary_op_helper2::enable::type \ name(const T& t, const U& u, const V& v, const W& w) const \ { \ return flint::funcname(*this, t, u, v, w); \ } #define FLINTXX_DEFINE_MEMBER_5OP(name) \ FLINTXX_DEFINE_MEMBER_5OP_(name, name) // Helper macros for FLINT_DEFINE_*_COND?. #define FLINTXX_COND_S(Base) flint_classes::is_source_base::template type #define FLINTXX_COND_T(Base) flint_classes::is_target_base::template type // Convenience rules. These all take a Base class as argument, and will // automatically apply to the related reference types as well. // Add a to_string() conversion rule, empolying the common flint idiom where // the string is allocated by the to_string function. #define FLINTXX_DEFINE_TO_STR(Base, eval) \ template \ struct to_string >::type> \ { \ static std::string get(const T& from, int base) \ { \ char* str = eval; \ std::string res(str); \ flint_free(str); \ return res; \ } \ }; // Add a swap rule. #define FLINTXX_DEFINE_SWAP(Base, eval) \ template \ struct swap, FLINTXX_COND_T(Base) > >::type> \ { \ static void doit(T& e1, U& e2) \ { \ eval; \ } \ }; // Define a conversion rule through a default-constructed temporary object. #define FLINTXX_DEFINE_CONVERSION_TMP(totype, Base, eval) \ template \ struct conversion >::type> \ { \ static totype get(const T& from) \ { \ totype to; \ eval; \ return to; \ } \ }; // Define a cmp rule. #define FLINTXX_DEFINE_CMP(Base, eval) \ template \ struct cmp, \ FLINTXX_COND_S(Base) > >::type> \ { \ static int get(const T& e1, const U& e2) \ { \ return eval; \ } \ }; // Define an equals rule. #define FLINTXX_DEFINE_EQUALS(Base, eval) \ template \ struct equals, FLINTXX_COND_S(Base) > >::type> \ { \ static bool get(const T& e1, const U& e2) \ { \ return eval; \ } \ }; // Define a string assignment rule (c/f many polynomial classes). #define FLINTXX_DEFINE_ASSIGN_STR(Base, eval) \ template \ struct assignment, traits::is_string > >::type> \ { \ static void doit(T& to, const char* from) \ { \ eval; \ } \ }; #define FLINTXX_UNADORNED_MAKETYPES(Base, left, op, right) \ Base##_expression< op, tuple< left, tuple< right, empty_tuple> > > // Optimized evaluation rules using ternary arithmetic (addmul, submul) // NB: this has to be called in namespace flint, not flint::rules! #define FLINTXX_DEFINE_TERNARY(Base, addmuleval, submuleval, maketypes) \ namespace rules { \ /* a +- b*c */ \ template \ struct evaluation, \ mp::equal_types >, \ empty_tuple>::type> >, \ true, 1, \ typename tools::ternary_helper::enable::type> \ { \ /* Helpful for testing. */ \ static const unsigned TERNARY_OP_MARKER = 0; \ \ typedef Base return_t; \ typedef tools::ternary_helper th; \ typedef typename th::temporaries_t temporaries_t; \ typedef tuple > data_t; \ static const bool is_add = mp::equal_types::val; \ \ static void doit(const data_t& input, temporaries_t temps, return_t* res) \ { \ const Base* left = 0; \ const Base* right = 0; \ th::doit(input.first(), input.second()._data().first(), \ input.second()._data().second(), temps, res, right, left); \ const Base& e1 = *left; \ const Base& e2 = *right; \ Base& to = *res; \ if(is_add) \ { \ addmuleval; \ } \ else \ { \ submuleval; \ } \ } \ }; \ \ /* b*c + a */ \ template \ struct evaluation >, \ true, 1, \ typename tools::ternary_helper::enable::type> \ { \ /* Helpful for testing. */ \ static const unsigned TERNARY_OP_MARKER = 0; \ \ typedef Base return_t; \ typedef tools::ternary_helper th; \ typedef typename th::temporaries_t temporaries_t; \ typedef tuple > data_t; \ \ static void doit(const data_t& input, temporaries_t temps, return_t* res) \ { \ const Base* left = 0; \ const Base* right = 0; \ th::doit(input.second(), input.first()._data().first(), \ input.first()._data().second(), temps, res, right, left); \ const Base& e1 = *left; \ const Base& e2 = *right; \ Base& to = *res; \ addmuleval; \ } \ }; \ } /* rules */ \ \ /* TODO enable these with references on left hand side(?) */ \ /* a += b*c */ \ template \ inline typename flint_classes::enable_ternary_assign::type \ operator+=(Base& to, \ const maketypes(Base, Right1, operations::times, Right2)& other) \ { \ flint_classes::ternary_assign_helper tah( \ other._data()); \ const Base& e1 = tah.getleft(); \ const Base& e2 = tah.getright(); \ addmuleval; \ return to; \ } \ \ /* a -= b*c */ \ template \ inline typename flint_classes::enable_ternary_assign::type \ operator-=(Base& to, \ const maketypes(Base, Right1, operations::times, Right2)& other) \ { \ flint_classes::ternary_assign_helper tah( \ other._data()); \ const Base& e1 = tah.getleft(); \ const Base& e2 = tah.getright(); \ submuleval; \ return to; \ } #endif