1 /* 2 Copyright (C) 2013 Tom Bachmann 3 4 This file is part of FLINT. 5 6 FLINT is free software: you can redistribute it and/or modify it under 7 the terms of the GNU Lesser General Public License (LGPL) as published 8 by the Free Software Foundation; either version 2.1 of the License, or 9 (at your option) any later version. See <http://www.gnu.org/licenses/>. 10 */ 11 12 // Helpers to define concrete subclasses of expression. 13 // Contrary to other parts of this library, they are tailored very 14 // specifically towards FLINT. 15 16 #include "../flint.h" 17 #include "mp.h" 18 #include "expression.h" 19 #include "expression_traits.h" 20 #include "evaluation_tools.h" 21 #include "tuple.h" 22 23 #ifndef CXX_FLINT_CLASSES_H 24 #define CXX_FLINT_CLASSES_H 25 26 // Flint classes distinguish themselves from "ordinary" expression template 27 // classes by a public typedef IS_FLINT_CLASS (see also FLINTXX_DEFINE_BASICS 28 // below). Most functionality in this header disables itself when used on a 29 // non-flint class. 30 31 // For all flint classes, Data of immediates must have typedefs data_ref_t and 32 // data_srcref_t. 33 34 // The immediates of any flint class come in three "flavours": ordinary, ref 35 // and srcref. Most of the classes below are used to convert these flavours. 36 // In order for this to work, the expression template class must contain a 37 // public typedef c_base_t which is the underlying "basic" C type (so not the 38 // length one array that is usually used), e.g. fmpz_poly_struct. Conversion to 39 // reference type then yields expression templates which have Data set to 40 // ref_data<Wrapped, c_base_t> or srcref_data. These implementation work as 41 // long as all data is stored in c_base_t. If not (e.g. for padic, where there 42 // is an additional reference to the context), ref_data and srcref_data have to 43 // be specialised appropriately. 44 45 namespace flint { 46 namespace flint_classes { 47 template<class Wrapped, class Inner> 48 struct ref_data 49 { 50 typedef void IS_REF_OR_CREF; 51 typedef Wrapped wrapped_t; 52 53 typedef Inner* data_ref_t; 54 typedef const Inner* data_srcref_t; 55 56 Inner* inner; 57 ref_dataref_data58 ref_data(Wrapped& o) : inner(o._data().inner) {} 59 makeref_data60 static ref_data make(Inner* f) {return ref_data(f);} 61 62 private: ref_dataref_data63 ref_data(Inner* fp) : inner(fp) {} 64 }; 65 66 template<class Wrapped, class Ref, class Inner> 67 struct srcref_data 68 { 69 typedef void IS_REF_OR_CREF; 70 typedef Wrapped wrapped_t; 71 72 typedef const Inner* data_ref_t; 73 typedef const Inner* data_srcref_t; 74 75 const Inner* inner; 76 srcref_datasrcref_data77 srcref_data(const Wrapped& o) : inner(o._data().inner) {} srcref_datasrcref_data78 srcref_data(Ref o) : inner(o._data().inner) {} 79 makesrcref_data80 static srcref_data make(const Inner* f) {return srcref_data(f);} 81 82 private: srcref_datasrcref_data83 srcref_data(const Inner* fp) : inner(fp) {} 84 }; 85 86 // Helper to determine if T is a flint class. 87 template<class T, class Enable = void> struct is_flint_class : mp::false_ { }; 88 template<class T> 89 struct is_flint_class<T, typename T::IS_FLINT_CLASS> : mp::true_ { }; 90 91 // From a lazy or immediate flint expression, obtain the evaluated 92 // non-reference type. 93 // Examples: fmpzxx -> fmpzxx 94 // fmpzxx_ref -> fmpzxx 95 // fmpzxx + fmpzxx -> fmpzxx 96 template<class T, class Enable = void> 97 struct to_nonref {typedef typename T::evaluated_t type;}; 98 99 template<class T> 100 struct to_nonref<T, typename T::data_t::IS_REF_OR_CREF> 101 { 102 typedef typename T::data_t::wrapped_t type; 103 }; 104 105 template<class T> 106 struct c_base_t 107 { 108 typedef typename T::c_base_t type; 109 }; 110 111 // Given a lazy or non-lazy flint expression, obtain th evaluated reference 112 // type. 113 // Examples: fmpzxx -> fmpzxx_ref 114 // fmpzxx_ref -> fmpzxx_ref 115 // fmpzxx + fmpzxx -> fmpzxx_ref 116 template<class T> 117 struct to_ref 118 { 119 typedef typename T::template make_helper<operations::immediate, ref_data< 120 typename to_nonref<T>::type, typename c_base_t<T>::type> >::type type; 121 }; 122 // Similarly for srcref. 123 template<class T> 124 struct to_srcref 125 { 126 typedef typename T::template make_helper<operations::immediate, srcref_data< 127 typename to_nonref<T>::type, typename to_ref<T>::type, 128 typename c_base_t<T>::type> >::type type; 129 }; 130 131 // Compute if Ref if the reference type belonging to compare. 132 // Examples: fmpzxx_ref, fmpzxx + fmpzxx -> true_ 133 // fmpzxx_srcref, fmpzxx -> false_ 134 template<class Compare, class Ref> 135 struct is_ref : mp::equal_types<Ref, typename to_ref<Compare>::type> { }; 136 137 // Similarly for srcref. 138 template<class Compare, class Ref> 139 struct is_srcref : mp::equal_types<Ref, typename to_srcref<Compare>::type> { }; 140 141 // Similarly for non-ref. 142 template<class T> 143 struct is_nonref : mp::equal_types<T, typename to_nonref<T>::type > { }; 144 145 // Flint classes allow implicit conversion only in very special situations. 146 // This template determines when. Currently, it is used exclusively to allow 147 // implicit conversion to reference types. 148 template<class T, class U, class Enable = void> 149 struct enableimplicit : mp::false_ { }; 150 151 template<class T, class U> 152 struct enableimplicit<T, U, typename mp::enable_if<mp::and_< 153 is_flint_class<T>, is_flint_class<U> 154 > >::type> 155 : mp::and_< 156 traits::is_immediate_expr<T>, 157 traits::is_immediate_expr<U>, 158 mp::or_< 159 mp::and_<is_ref<U, T>, is_nonref<U> >, 160 mp::and_<is_srcref<U, T>, is_nonref<U> >, 161 mp::and_<is_srcref<U, T>, is_ref<U, U> > 162 > > { }; 163 164 // Helper template which allows accessing data_(src)ref_t on immediates, 165 // without causing a compiler error on non-immediates. 166 // The main use for this are the _fmpz(), _fmpq() etc methods, which only 167 // work on immediates (but are defined on all instances). 168 template<class Expr, class Enable = void> 169 struct maybe_data_ref 170 { 171 typedef void data_ref_t; 172 typedef void data_srcref_t; 173 }; 174 template<class Expr> 175 struct maybe_data_ref<Expr, 176 typename mp::enable_if< 177 // NB: cannot use is_immediate, since Expr may be incomplete type! 178 mp::equal_types<typename Expr::operation_t, operations::immediate> >::type> 179 { 180 typedef typename Expr::data_t::data_ref_t data_ref_t; 181 typedef typename Expr::data_t::data_srcref_t data_srcref_t; 182 }; 183 184 // If Base is a non-ref flint class, determine if T is a source operand 185 // (i.e. non-ref, ref or srcref type belong to Base) 186 // Examples: fmpzxx, fmpzxx_srcref -> true 187 // fmpzxx, fmpzxx -> true 188 // fmpzxx, fmpqxx -> false 189 template<class Base, class T, class Enable = void> 190 struct is_source : mp::false_ { }; 191 template<class Base, class T> 192 struct is_source<Base, T, typename mp::enable_if< 193 mp::and_<is_flint_class<T>, is_flint_class<Base> > >::type> 194 : mp::or_<mp::equal_types<T, Base>, is_ref<Base, T>, is_srcref<Base, T> > { }; 195 196 // Same with target (i.e. disallow srcref). 197 template<class Base, class T, class Enable = void> 198 struct is_target : mp::false_ { }; 199 template<class Base, class T> 200 struct is_target<Base, T, typename mp::enable_if< 201 mp::and_<is_flint_class<T>, is_flint_class<Base> > >::type> 202 : mp::or_<mp::equal_types<T, Base>, is_ref<Base, T> > { }; 203 204 // Predicate version of the above. Useful for FLINT_DEFINE_*_COND. 205 // See FLINTXX_COND_S and FLINTXX_COND_T 206 template<class Base> 207 struct is_source_base 208 { 209 template<class T> struct type : is_source<Base, T> { }; 210 }; 211 template<class Base> 212 struct is_target_base 213 { 214 template<class T> struct type : is_target<Base, T> { }; 215 }; 216 217 // Helper for implementing x += y*z etc 218 template<class T, class Right1, class Right2> 219 struct ternary_assign_helper 220 { 221 typedef typename mp::make_tuple<Right1, Right2>::type tup_t; 222 typedef tools::evaluate_n<tup_t> ev2_t; 223 typedef typename ev2_t::temporaries_t temporaries_t; 224 typedef mp::back_tuple<temporaries_t> back_t; 225 226 typename back_t::type backing; 227 ev2_t ev2; 228 229 static temporaries_t backtemps(typename back_t::type& backing) 230 { 231 temporaries_t temps; 232 back_t::init(temps, backing); 233 return temps; 234 } 235 236 ternary_assign_helper(const tup_t& tup) 237 : backing(mp::htuples::fill<typename back_t::type>( 238 tools::temporaries_filler( 239 tup.first()+tup.second() /* XXX */))), 240 ev2(tup, backtemps(backing)) {} 241 const T& getleft() {return ev2.template get<0>();} 242 const T& getright() {return ev2.template get<1>();} 243 }; 244 245 template<class T, class Right1, class Right2> 246 struct enable_ternary_assign 247 : mp::enable_if<mp::and_< 248 traits::is_T_expr<typename traits::basetype<Right1>::type, T>, 249 traits::is_T_expr<typename traits::basetype<Right2>::type, T> >, T&> { }; 250 251 // convenience helper 252 template<class Base, class T> struct is_Base : mp::or_< 253 traits::is_T_expr<T, Base>, 254 is_source<Base, T> > { }; 255 } // flint_classes 256 257 namespace traits { 258 // Enable evaluation into reference types. See can_evaluate_into in 259 // expression_traits.h. 260 // XXX why do we need to disable the case where T, U are equal? 261 // Is <T, T> not more special? 262 template<class T, class U> 263 struct can_evaluate_into<T, U, 264 typename mp::enable_if<mp::and_<flint_classes::is_flint_class<T>, 265 flint_classes::is_flint_class<U>, 266 mp::not_<mp::equal_types<T, U> > > >::type> 267 : flint_classes::is_ref<U, T> { }; 268 } // traits 269 270 271 namespace detail { 272 template<class Expr, class Enable = void> 273 struct should_enable_extra_ternop : mp::false_ { }; 274 template<class Expr> 275 struct should_enable_extra_ternop<Expr, typename mp::enable_if< 276 flint_classes::is_flint_class<Expr> >::type> 277 : mp::equal_types<Expr, typename flint_classes::to_ref< 278 typename flint_classes::to_nonref<Expr>::type>::type> { }; 279 } // detail 280 281 // We add additional overloads for when the LHS is a reference type. The 282 // problem is that the standard overloads take LHS via reference, and rvalues 283 // (such as coming from fmpz_polyxx::get_coeff()) 284 // cannot bind to this. In this case instead objects should be taken by value. 285 // However, this will make the overload ambiguous. Hence we take by const 286 // reference and then make an additional copy. 287 template<class Expr1, class Expr2> 288 inline typename mp::enable_if<detail::should_enable_extra_ternop<Expr1>, Expr1>::type 289 operator+=(const Expr1& e1, const Expr2& e2) 290 { 291 Expr1(e1).set(e1 + e2); 292 return e1; 293 } 294 template<class Expr1, class Expr2> 295 inline typename mp::enable_if<detail::should_enable_extra_ternop<Expr1>, Expr1>::type 296 operator-=(const Expr1& e1, const Expr2& e2) 297 { 298 Expr1(e1).set(e1 - e2); 299 return e1; 300 } 301 template<class Expr1, class Expr2> 302 inline typename mp::enable_if<detail::should_enable_extra_ternop<Expr1>, Expr1>::type 303 operator*=(const Expr1& e1, const Expr2& e2) 304 { 305 Expr1(e1).set(e1 * e2); 306 return e1; 307 } 308 template<class Expr1, class Expr2> 309 inline typename mp::enable_if<detail::should_enable_extra_ternop<Expr1>, Expr1>::type 310 operator/=(const Expr1& e1, const Expr2& e2) 311 { 312 Expr1(e1).set(e1 / e2); 313 return e1; 314 } 315 template<class Expr1, class Expr2> 316 inline typename mp::enable_if<detail::should_enable_extra_ternop<Expr1>, Expr1>::type 317 operator%=(const Expr1& e1, const Expr2& e2) 318 { 319 Expr1(e1).set(e1 % e2); 320 return e1; 321 } 322 template<class Expr1, class Expr2> 323 inline typename mp::enable_if<detail::should_enable_extra_ternop<Expr1>, Expr1>::type 324 operator<<=(const Expr1& e1, const Expr2& e2) 325 { 326 Expr1(e1).set(e1 << e2); 327 return e1; 328 } 329 template<class Expr1, class Expr2> 330 inline typename mp::enable_if<detail::should_enable_extra_ternop<Expr1>, Expr1>::type 331 operator>>=(const Expr1& e1, const Expr2& e2) 332 { 333 Expr1(e1).set(e1 >> e2); 334 return e1; 335 } 336 } // flint 337 338 // macros that help defining flint classes 339 340 #define FLINTXX_DEFINE_BASICS_NOFLINTCLASS(name) \ 341 public: \ 342 typedef typename base_t::evaluated_t evaluated_t; \ 343 \ 344 template<class T> \ 345 struct doimplicit \ 346 : flint_classes::enableimplicit<name, T> { }; \ 347 \ 348 template<class T> \ 349 name& operator=(const T& t) \ 350 { \ 351 this->set(t); \ 352 return *this; \ 353 } \ 354 \ 355 protected: \ 356 explicit name(const Data& d) : base_t(d) {} \ 357 \ 358 template<class D, class O, class Da> \ 359 friend class expression; 360 361 // all flint classes should have this 362 #define FLINTXX_DEFINE_BASICS(name) \ 363 public: \ 364 typedef void IS_FLINT_CLASS; \ 365 FLINTXX_DEFINE_BASICS_NOFLINTCLASS(name) \ 366 367 // all flint classes should have this 368 #define FLINTXX_DEFINE_CTORS(name) \ 369 public: \ 370 name() : base_t() {} \ 371 template<class T> \ 372 explicit name(const T& t, \ 373 typename mp::disable_if<doimplicit<T> >::type* = 0) \ 374 : base_t(t) {} \ 375 template<class T> \ 376 explicit name(T& t, \ 377 typename mp::disable_if<doimplicit<T> >::type* = 0) \ 378 : base_t(t) {} \ 379 template<class T> \ 380 name(const T& t, \ 381 typename mp::enable_if<doimplicit<T> >::type* = 0) \ 382 : base_t(t) {} \ 383 template<class T> \ 384 name(T& t, \ 385 typename mp::enable_if<doimplicit<T> >::type* = 0) \ 386 : base_t(t) {} \ 387 template<class T, class U> \ 388 name(const T& t, const U& u) : base_t(t, u) {} \ 389 template<class T, class U> \ 390 name(T& t, const U& u) : base_t(t, u) {} \ 391 template<class T, class U, class V> \ 392 name(const T& t, const U& u, const V& v) : base_t(t, u, v) {} \ 393 template<class T, class U, class V> \ 394 name(T& t, const U& u, const V& v) : base_t(t, u, v) {} \ 395 template<class T, class U, class V, class W> \ 396 name(const T& t, const U& u, const V& v, const W& w) \ 397 : base_t(t, u, v, w) {} \ 398 template<class T, class U, class V, class W> \ 399 name(T& t, const U& u, const V& v, const W& w) \ 400 : base_t(t, u, v, w) {} 401 402 // Enable the flint reference type scheme. This typedefs c_base_t to ctype, 403 // and adds the data access wrapper (like _fmpz(), _fmpq()) called accessname. 404 // It also provides reference constructors from C types. 405 // All flint classes should have this. 406 #define FLINTXX_DEFINE_C_REF(name, ctype, accessname) \ 407 public: \ 408 typedef ctype c_base_t; \ 409 typedef flint_classes::maybe_data_ref<name> wrapped_traits; \ 410 typename wrapped_traits::data_ref_t accessname() \ 411 { \ 412 return this->_data().inner; \ 413 } \ 414 typename wrapped_traits::data_srcref_t accessname() const \ 415 { \ 416 return this->_data().inner; \ 417 } \ 418 \ 419 /* These only make sense with the reference types */ \ 420 template<class T> \ 421 static name make(T& f) \ 422 { \ 423 return name(Data::make(f)); \ 424 } \ 425 template<class T> \ 426 static name make(const T& f) \ 427 { \ 428 return name(Data::make(f)); \ 429 } \ 430 template<class T, class U> \ 431 static name make(T& f, const U& u) \ 432 { \ 433 return name(Data::make(f, u)); \ 434 } \ 435 template<class T, class U> \ 436 static name make(const T& f, const U& u) \ 437 { \ 438 return name(Data::make(f, u)); \ 439 } \ 440 template<class T, class U, class V> \ 441 static name make(const T& f, const U& u, const V& v) \ 442 { \ 443 return name(Data::make(f, u, v)); \ 444 } 445 446 // Add a statically forwarded constructor called name. (Forwarded to data_t). 447 #define FLINTXX_DEFINE_FORWARD_STATIC(name) \ 448 template<class T> \ 449 static typename base_t::derived_t name(const T& f) \ 450 { \ 451 return typename base_t::derived_t(Data::name(f)); \ 452 } \ 453 template<class T, class U> \ 454 static typename base_t::derived_t name(const T& f, const U& u) \ 455 { \ 456 return typename base_t::derived_t(Data::name(f, u)); \ 457 } 458 459 // Add a static randomisation function. 460 // XXX this is not really useful because the arguments are often different. 461 #define FLINTXX_DEFINE_RANDFUNC(CBase, name) \ 462 static CBase##xx_expression name(frandxx& state, flint_bitcnt_t bits) \ 463 { \ 464 CBase##xx_expression res; \ 465 CBase##_##name(res._data().inner, state._data(), bits); \ 466 return res; \ 467 } 468 469 // Add a forwarded unary operation to the class. Suppose there is a unary 470 // operation foo() which returns my_typeA, and takes an argument of type 471 // my_typeB. Now on instances my_typeB, you want to write x.bar() for foo(x). 472 // Then add FLINTXX_DEFINE_MEMBER_UNOP_RTYPE_(my_typeA, bar, foo) to my_typeB. 473 // 474 // XXX due to circular definition problems, this cannot use the usual 475 // unary_op_helper type approach, and the unop must return the same type 476 // of expression 477 #define FLINTXX_DEFINE_MEMBER_UNOP_RTYPE_(rettype, name, funcname) \ 478 FLINT_UNOP_BUILD_RETTYPE(funcname, rettype, typename base_t::derived_t) \ 479 name() const \ 480 { \ 481 return flint::funcname(*this); \ 482 } 483 484 // Convenience version when name==funcname 485 #define FLINTXX_DEFINE_MEMBER_UNOP_RTYPE(rettype, name) \ 486 FLINTXX_DEFINE_MEMBER_UNOP_RTYPE_(rettype, name, name) 487 488 // Convenience version when rettype==argtype 489 #define FLINTXX_DEFINE_MEMBER_UNOP_(name, funcname) \ 490 FLINTXX_DEFINE_MEMBER_UNOP_RTYPE_(typename base_t::derived_t, name, funcname) 491 492 // Convenience version when rettype==argtype and name==funcname 493 #define FLINTXX_DEFINE_MEMBER_UNOP(name) FLINTXX_DEFINE_MEMBER_UNOP_(name, name) 494 495 // Add a forwarded binary operation. It is not necessary to specify the return 496 // type. 497 #define FLINTXX_DEFINE_MEMBER_BINOP_(name, funcname) \ 498 template<class T> \ 499 typename detail::binary_op_helper<typename base_t::derived_t, \ 500 operations::funcname##_op, T>::enable::type \ 501 name(const T& t) const \ 502 { \ 503 return flint::funcname(*this, t); \ 504 } 505 506 // Convenience version when funcname==name. 507 #define FLINTXX_DEFINE_MEMBER_BINOP(name) \ 508 FLINTXX_DEFINE_MEMBER_BINOP_(name, name) 509 510 #define FLINTXX_DEFINE_MEMBER_3OP_(name, funcname) \ 511 template<class T, class U> \ 512 typename detail::nary_op_helper2<operations::funcname##_op, \ 513 typename base_t::derived_t, T, U>::enable::type \ 514 name(const T& t, const U& u) const \ 515 { \ 516 return flint::funcname(*this, t, u); \ 517 } 518 519 #define FLINTXX_DEFINE_MEMBER_3OP(name) \ 520 FLINTXX_DEFINE_MEMBER_3OP_(name, name) 521 522 #define FLINTXX_DEFINE_MEMBER_4OP_(name, funcname) \ 523 template<class T, class U, class V> \ 524 typename detail::nary_op_helper2<operations::funcname##_op, \ 525 typename base_t::derived_t, T, U, V>::enable::type \ 526 name(const T& t, const U& u, const V& v) const \ 527 { \ 528 return flint::funcname(*this, t, u, v); \ 529 } 530 531 #define FLINTXX_DEFINE_MEMBER_4OP(name) \ 532 FLINTXX_DEFINE_MEMBER_4OP_(name, name) 533 534 #define FLINTXX_DEFINE_MEMBER_5OP_(name, funcname) \ 535 template<class T, class U, class V, class W> \ 536 typename detail::nary_op_helper2<operations::funcname##_op, \ 537 typename base_t::derived_t, T, U, V, W>::enable::type \ 538 name(const T& t, const U& u, const V& v, const W& w) const \ 539 { \ 540 return flint::funcname(*this, t, u, v, w); \ 541 } 542 543 #define FLINTXX_DEFINE_MEMBER_5OP(name) \ 544 FLINTXX_DEFINE_MEMBER_5OP_(name, name) 545 546 // Helper macros for FLINT_DEFINE_*_COND?. 547 #define FLINTXX_COND_S(Base) flint_classes::is_source_base<Base>::template type 548 #define FLINTXX_COND_T(Base) flint_classes::is_target_base<Base>::template type 549 550 // Convenience rules. These all take a Base class as argument, and will 551 // automatically apply to the related reference types as well. 552 553 // Add a to_string() conversion rule, empolying the common flint idiom where 554 // the string is allocated by the to_string function. 555 #define FLINTXX_DEFINE_TO_STR(Base, eval) \ 556 template<class T> \ 557 struct to_string<T, \ 558 typename mp::enable_if< FLINTXX_COND_S(Base)<T> >::type> \ 559 { \ 560 static std::string get(const T& from, int base) \ 561 { \ 562 char* str = eval; \ 563 std::string res(str); \ 564 flint_free(str); \ 565 return res; \ 566 } \ 567 }; 568 569 // Add a swap rule. 570 #define FLINTXX_DEFINE_SWAP(Base, eval) \ 571 template<class T, class U> \ 572 struct swap<T, U, typename mp::enable_if< mp::and_< \ 573 FLINTXX_COND_T(Base)<T>, FLINTXX_COND_T(Base)<U> > >::type> \ 574 { \ 575 static void doit(T& e1, U& e2) \ 576 { \ 577 eval; \ 578 } \ 579 }; 580 581 // Define a conversion rule through a default-constructed temporary object. 582 #define FLINTXX_DEFINE_CONVERSION_TMP(totype, Base, eval) \ 583 template<class T> \ 584 struct conversion<totype, T, \ 585 typename mp::enable_if< FLINTXX_COND_S(Base)<T> >::type> \ 586 { \ 587 static totype get(const T& from) \ 588 { \ 589 totype to; \ 590 eval; \ 591 return to; \ 592 } \ 593 }; 594 595 // Define a cmp rule. 596 #define FLINTXX_DEFINE_CMP(Base, eval) \ 597 template<class T, class U> \ 598 struct cmp<T, U, \ 599 typename mp::enable_if<mp::and_<FLINTXX_COND_S(Base)<T>, \ 600 FLINTXX_COND_S(Base)<U> > >::type> \ 601 { \ 602 static int get(const T& e1, const U& e2) \ 603 { \ 604 return eval; \ 605 } \ 606 }; 607 608 // Define an equals rule. 609 #define FLINTXX_DEFINE_EQUALS(Base, eval) \ 610 template<class T, class U> \ 611 struct equals<T, U, typename mp::enable_if<mp::and_< \ 612 FLINTXX_COND_S(Base)<T>, FLINTXX_COND_S(Base)<U> > >::type> \ 613 { \ 614 static bool get(const T& e1, const U& e2) \ 615 { \ 616 return eval; \ 617 } \ 618 }; 619 620 // Define a string assignment rule (c/f many polynomial classes). 621 #define FLINTXX_DEFINE_ASSIGN_STR(Base, eval) \ 622 template<class T, class U> \ 623 struct assignment<T, U, \ 624 typename mp::enable_if<mp::and_< \ 625 FLINTXX_COND_T(Base)<T>, traits::is_string<U> > >::type> \ 626 { \ 627 static void doit(T& to, const char* from) \ 628 { \ 629 eval; \ 630 } \ 631 }; 632 633 #define FLINTXX_UNADORNED_MAKETYPES(Base, left, op, right) \ 634 Base##_expression< op, tuple< left, tuple< right, empty_tuple> > > 635 636 // Optimized evaluation rules using ternary arithmetic (addmul, submul) 637 // NB: this has to be called in namespace flint, not flint::rules! 638 #define FLINTXX_DEFINE_TERNARY(Base, addmuleval, submuleval, maketypes) \ 639 namespace rules { \ 640 /* a +- b*c */ \ 641 template<class Op, class Left, class Right1, class Right2> \ 642 struct evaluation<Op, \ 643 tuple<Left, tuple< \ 644 maketypes(Base, Right1, operations::times, Right2), \ 645 /* NB: there is no particular reason to have the enable_if here, \ 646 many other similar places would do */ \ 647 typename mp::enable_if<mp::or_< \ 648 mp::equal_types<Op, operations::plus>, \ 649 mp::equal_types<Op, operations::minus> >, \ 650 empty_tuple>::type> >, \ 651 true, 1, \ 652 typename tools::ternary_helper<Base, Left, Right1, Right2>::enable::type> \ 653 { \ 654 /* Helpful for testing. */ \ 655 static const unsigned TERNARY_OP_MARKER = 0; \ 656 \ 657 typedef Base return_t; \ 658 typedef tools::ternary_helper<Base, Left, Right1, Right2> th; \ 659 typedef typename th::temporaries_t temporaries_t; \ 660 typedef tuple<Left, tuple< \ 661 maketypes(Base, Right1, operations::times, Right2), \ 662 empty_tuple> > data_t; \ 663 static const bool is_add = mp::equal_types<Op, operations::plus>::val; \ 664 \ 665 static void doit(const data_t& input, temporaries_t temps, return_t* res) \ 666 { \ 667 const Base* left = 0; \ 668 const Base* right = 0; \ 669 th::doit(input.first(), input.second()._data().first(), \ 670 input.second()._data().second(), temps, res, right, left); \ 671 const Base& e1 = *left; \ 672 const Base& e2 = *right; \ 673 Base& to = *res; \ 674 if(is_add) \ 675 { \ 676 addmuleval; \ 677 } \ 678 else \ 679 { \ 680 submuleval; \ 681 } \ 682 } \ 683 }; \ 684 \ 685 /* b*c + a */ \ 686 template<class Right, class Left1, class Left2> \ 687 struct evaluation<operations::plus, \ 688 tuple<maketypes(Base, Left1, operations::times, Left2), \ 689 tuple<Right, empty_tuple> >, \ 690 true, 1, \ 691 typename tools::ternary_helper<Base, \ 692 Right, Left1, Left2, operations::times>::enable::type> \ 693 { \ 694 /* Helpful for testing. */ \ 695 static const unsigned TERNARY_OP_MARKER = 0; \ 696 \ 697 typedef Base return_t; \ 698 typedef tools::ternary_helper<Base, Right, Left1, Left2> th; \ 699 typedef typename th::temporaries_t temporaries_t; \ 700 typedef tuple<maketypes(Base, Left1, operations::times, Left2), \ 701 tuple<Right, empty_tuple> > data_t; \ 702 \ 703 static void doit(const data_t& input, temporaries_t temps, return_t* res) \ 704 { \ 705 const Base* left = 0; \ 706 const Base* right = 0; \ 707 th::doit(input.second(), input.first()._data().first(), \ 708 input.first()._data().second(), temps, res, right, left); \ 709 const Base& e1 = *left; \ 710 const Base& e2 = *right; \ 711 Base& to = *res; \ 712 addmuleval; \ 713 } \ 714 }; \ 715 } /* rules */ \ 716 \ 717 /* TODO enable these with references on left hand side(?) */ \ 718 /* a += b*c */ \ 719 template<class Right1, class Right2> \ 720 inline typename flint_classes::enable_ternary_assign<Base, Right1, Right2>::type \ 721 operator+=(Base& to, \ 722 const maketypes(Base, Right1, operations::times, Right2)& other) \ 723 { \ 724 flint_classes::ternary_assign_helper<Base, Right1, Right2> tah( \ 725 other._data()); \ 726 const Base& e1 = tah.getleft(); \ 727 const Base& e2 = tah.getright(); \ 728 addmuleval; \ 729 return to; \ 730 } \ 731 \ 732 /* a -= b*c */ \ 733 template<class Right1, class Right2> \ 734 inline typename flint_classes::enable_ternary_assign<Base, Right1, Right2>::type \ 735 operator-=(Base& to, \ 736 const maketypes(Base, Right1, operations::times, Right2)& other) \ 737 { \ 738 flint_classes::ternary_assign_helper<Base, Right1, Right2> tah( \ 739 other._data()); \ 740 const Base& e1 = tah.getleft(); \ 741 const Base& e2 = tah.getright(); \ 742 submuleval; \ 743 return to; \ 744 } 745 746 #endif 747