962 lines
30 KiB
C
962 lines
30 KiB
C
|
/*=============================================================================
|
||
|
|
||
|
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
|
||
|
|
||
|
******************************************************************************/
|
||
|
|
||
|
#ifndef CXX_EXPRESSION_H
|
||
|
#define CXX_EXPRESSION_H
|
||
|
|
||
|
// TODO
|
||
|
// * static asserts
|
||
|
|
||
|
#include <iosfwd>
|
||
|
#include <string>
|
||
|
#include <cstdio>
|
||
|
|
||
|
#include "evaluation_tools.h"
|
||
|
#include "expression_traits.h"
|
||
|
#include "mp.h"
|
||
|
#include "rules.h"
|
||
|
#include "traits.h"
|
||
|
#include "tuple.h"
|
||
|
|
||
|
namespace flint {
|
||
|
namespace detail {
|
||
|
// Helper traits used by the "expression" class, in particular the evaluate()
|
||
|
// method. This is the general (i.e. non-immediate) case,
|
||
|
// which requires actual work.
|
||
|
template<class Operation, class Expr, class Data>
|
||
|
struct evaluation_traits
|
||
|
{
|
||
|
typedef typename Expr::derived_t derived_t;
|
||
|
typedef typename mp::find_evaluation<
|
||
|
Operation, Data, false>::type rule_t;
|
||
|
typedef typename mp::find_evaluation<
|
||
|
Operation, Data, true>::type temp_rule_t;
|
||
|
typedef typename rule_t::return_t evaluation_return_t;
|
||
|
typedef evaluation_return_t evaluated_t;
|
||
|
|
||
|
static evaluation_return_t evaluate(const derived_t& from)
|
||
|
{
|
||
|
evaluated_t res =
|
||
|
rules::instantiate_temporaries<derived_t, evaluated_t>::get(from);
|
||
|
evaluate_into_fresh(res, from);
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
template<class T>
|
||
|
static void evaluate_into(T& to, const derived_t& from)
|
||
|
{
|
||
|
typedef mp::back_tuple<typename rule_t::temporaries_t> back_t;
|
||
|
typename back_t::type temps_backing =
|
||
|
mp::htuples::fill<typename back_t::type>(
|
||
|
tools::temporaries_filler(from));
|
||
|
typename rule_t::temporaries_t temps;
|
||
|
back_t::init(temps, temps_backing, 0);
|
||
|
rule_t::doit(from._data(), temps, &to);
|
||
|
}
|
||
|
|
||
|
static void evaluate_into_fresh(evaluation_return_t& to, const derived_t& from)
|
||
|
{
|
||
|
typedef mp::back_tuple<
|
||
|
typename temp_rule_t::temporaries_t,
|
||
|
evaluation_return_t
|
||
|
> back_t;
|
||
|
typename back_t::type temps_backing =
|
||
|
mp::htuples::fill<typename back_t::type>(
|
||
|
tools::temporaries_filler(from));
|
||
|
typename temp_rule_t::temporaries_t temps;
|
||
|
back_t::init(temps, temps_backing, &to);
|
||
|
temp_rule_t::doit(from._data(), temps, &to);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// This is the special case of an immediate argument, where "evaluation" is
|
||
|
// at most assignment.
|
||
|
template<class Expr, class Data>
|
||
|
struct evaluation_traits<operations::immediate, Expr, Data>
|
||
|
{
|
||
|
typedef typename Expr::derived_t derived_t;
|
||
|
typedef typename Expr::derived_t evaluated_t;
|
||
|
typedef evaluated_t& evaluation_return_t;
|
||
|
|
||
|
static evaluated_t& evaluate(derived_t& d) {return d;}
|
||
|
static const evaluated_t& evaluate(const derived_t& d) {return d;}
|
||
|
|
||
|
template<class T>
|
||
|
static void evaluate_into(T& to, const derived_t& from)
|
||
|
{
|
||
|
rules::assignment<T, derived_t>::doit(to, from);
|
||
|
}
|
||
|
|
||
|
static void evaluate_into_fresh(derived_t& to, const derived_t& from)
|
||
|
{
|
||
|
evaluate_into(to, from);
|
||
|
}
|
||
|
};
|
||
|
} // detail
|
||
|
|
||
|
// The main expression template class.
|
||
|
//
|
||
|
// The argument Derived must have the following form:
|
||
|
// struct derived
|
||
|
// {
|
||
|
// template<class Operation, class Data>
|
||
|
// struct type
|
||
|
// {
|
||
|
// typedef XYZ result;
|
||
|
// };
|
||
|
// };
|
||
|
// See derived_wrapper below for a common example.
|
||
|
//
|
||
|
// Note that, while Data does not have to be default constructible,
|
||
|
// it *does* need to be copy-constructible, and have a working destructor.
|
||
|
template<class Derived, class Operation, class Data>
|
||
|
class expression
|
||
|
{
|
||
|
private:
|
||
|
Data data;
|
||
|
|
||
|
protected:
|
||
|
explicit expression(const Data& d) : data(d) {}
|
||
|
|
||
|
public:
|
||
|
// internal -- see is_expression implementation.
|
||
|
typedef void IS_EXPRESSION_MARKER;
|
||
|
|
||
|
typedef detail::evaluation_traits<Operation, expression, Data> ev_traits_t;
|
||
|
typedef typename Derived::template type<Operation, Data>::result derived_t;
|
||
|
typedef typename ev_traits_t::evaluated_t evaluated_t;
|
||
|
typedef typename ev_traits_t::evaluation_return_t evaluation_return_t;
|
||
|
typedef Data data_t;
|
||
|
typedef Operation operation_t;
|
||
|
|
||
|
private:
|
||
|
derived_t& downcast() {return *static_cast<derived_t*>(this);}
|
||
|
const derived_t& downcast() const
|
||
|
{
|
||
|
return *static_cast<const derived_t*>(this);
|
||
|
}
|
||
|
|
||
|
// Some helpers for initialization, since it is not possible to
|
||
|
// conditionally enable constructors in C++98
|
||
|
template<class T>
|
||
|
static data_t get_data(const T& t,
|
||
|
typename mp::disable_if<traits::is_lazy_expr<T> >::type* = 0)
|
||
|
{
|
||
|
return data_t(t);
|
||
|
}
|
||
|
template<class T>
|
||
|
static data_t get_data(T& t,
|
||
|
typename mp::disable_if<traits::is_lazy_expr<T> >::type* = 0)
|
||
|
{
|
||
|
return data_t(t);
|
||
|
}
|
||
|
template<class T>
|
||
|
static data_t get_data(const T& t,
|
||
|
typename mp::enable_if<traits::is_lazy_expr<T> >::type* = 0,
|
||
|
typename mp::disable_if<
|
||
|
mp::equal_types<typename T::evaluated_t, derived_t> >::type* = 0)
|
||
|
{
|
||
|
return data_t(t.evaluate());
|
||
|
}
|
||
|
template<class T>
|
||
|
static data_t get_data(const T& t,
|
||
|
typename mp::enable_if<traits::is_lazy_expr<T> >::type* = 0,
|
||
|
typename mp::enable_if<
|
||
|
mp::equal_types<typename T::evaluated_t, derived_t> >::type* = 0)
|
||
|
{
|
||
|
return data_t(t.evaluate()._data());
|
||
|
}
|
||
|
|
||
|
// Invoke the data copy constructor when appropriate
|
||
|
static data_t get_data(const derived_t& o)
|
||
|
{
|
||
|
return data_t(o._data());
|
||
|
}
|
||
|
static data_t get_data(derived_t& o)
|
||
|
{
|
||
|
return data_t(o._data());
|
||
|
}
|
||
|
|
||
|
// Having the empty constructor here delays its instantiation, and allows
|
||
|
// compiling even if data is *not* default constructible.
|
||
|
static data_t get_data() {return data_t();}
|
||
|
|
||
|
public:
|
||
|
// forwarded constructors
|
||
|
template<class T>
|
||
|
explicit expression(const T& t)
|
||
|
: data(get_data(t)) {}
|
||
|
|
||
|
template<class T>
|
||
|
explicit expression(T& t)
|
||
|
: data(get_data(t)) {}
|
||
|
|
||
|
template<class T, class U>
|
||
|
expression(const T& t, const U& u)
|
||
|
: data(t, u) {}
|
||
|
template<class T, class U>
|
||
|
expression(T& t, const U& u)
|
||
|
: data(t, u) {}
|
||
|
|
||
|
template<class T, class U, class V>
|
||
|
expression(const T& t, const U& u, const V& v)
|
||
|
: data(t, u, v) {}
|
||
|
template<class T, class U, class V>
|
||
|
expression(T& t, const U& u, const V& v)
|
||
|
: data(t, u, v) {}
|
||
|
template<class T, class U, class V, class W>
|
||
|
expression(const T& t, const U& u, const V& v, const W& w)
|
||
|
: data(t, u, v, w) {}
|
||
|
template<class T, class U, class V, class W>
|
||
|
expression(T& t, const U& u, const V& v, const W& w)
|
||
|
: data(t, u, v, w) {}
|
||
|
|
||
|
expression() : data(get_data()) {}
|
||
|
|
||
|
expression& operator=(const expression& o)
|
||
|
{
|
||
|
this->set(o.downcast());
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
// See rules::instantiate_temporaries for explanation.
|
||
|
evaluated_t create_temporary() const
|
||
|
{
|
||
|
return evaluated_t();
|
||
|
}
|
||
|
|
||
|
Data& _data() {return data;}
|
||
|
const Data& _data() const {return data;}
|
||
|
|
||
|
void print(std::ostream& o) const
|
||
|
{
|
||
|
tools::print_using_str<evaluated_t>::doit(evaluate(), o);
|
||
|
}
|
||
|
|
||
|
std::string to_string(int base = 10) const
|
||
|
{
|
||
|
return rules::to_string<evaluated_t>::get(evaluate(), base);
|
||
|
}
|
||
|
|
||
|
template<class T>
|
||
|
T to() const
|
||
|
{
|
||
|
return rules::conversion<T, evaluated_t>::get(evaluate());
|
||
|
}
|
||
|
|
||
|
int print(FILE* f = stdout) const
|
||
|
{
|
||
|
return rules::cprint<evaluated_t>::doit(f, evaluate());
|
||
|
}
|
||
|
int print_pretty(FILE* f = stdout) const
|
||
|
{
|
||
|
return rules::print_pretty<evaluated_t>::doit(f, evaluate());
|
||
|
}
|
||
|
template<class T>
|
||
|
int print_pretty(const T& extra, FILE* f = stdout) const
|
||
|
{
|
||
|
return rules::print_pretty<evaluated_t>::doit(f, evaluate(), extra);
|
||
|
}
|
||
|
int read(FILE* f = stdin)
|
||
|
{
|
||
|
return rules::read<derived_t>::doit(f, downcast());
|
||
|
}
|
||
|
|
||
|
typename traits::make_const<evaluation_return_t>::type evaluate() const
|
||
|
{
|
||
|
return ev_traits_t::evaluate(downcast());
|
||
|
}
|
||
|
evaluation_return_t evaluate() {return ev_traits_t::evaluate(downcast());}
|
||
|
|
||
|
template<class T>
|
||
|
void set(const T& t,
|
||
|
typename mp::enable_if<traits::is_expression<T> >::type* = 0,
|
||
|
typename mp::enable_if<traits::can_evaluate_into<
|
||
|
derived_t, typename T::evaluated_t> >::type* = 0)
|
||
|
{
|
||
|
T::ev_traits_t::evaluate_into(downcast(), t);
|
||
|
}
|
||
|
template<class T>
|
||
|
void set(const T& t,
|
||
|
typename mp::enable_if<traits::is_expression<T> >::type* = 0,
|
||
|
typename mp::disable_if<traits::can_evaluate_into<
|
||
|
derived_t, typename T::evaluated_t> >::type* = 0)
|
||
|
{
|
||
|
rules::assignment<derived_t, typename T::evaluated_t>::doit(
|
||
|
downcast(), t.evaluate());
|
||
|
}
|
||
|
template<class T>
|
||
|
void set(const T& t,
|
||
|
typename mp::disable_if<traits::is_expression<T> >::type* = 0)
|
||
|
{
|
||
|
rules::assignment<derived_t, T>::doit(downcast(), t);
|
||
|
}
|
||
|
|
||
|
template<class T>
|
||
|
bool equals(const T& t,
|
||
|
typename mp::enable_if<traits::is_lazy_expr<T> >::type* = 0) const
|
||
|
{
|
||
|
return equals(t.evaluate());
|
||
|
}
|
||
|
template<class T>
|
||
|
bool equals(const T& t,
|
||
|
typename mp::disable_if<traits::is_lazy_expr<T> >::type* = 0) const
|
||
|
{
|
||
|
return tools::equals_using_cmp<evaluated_t, T>::get(evaluate(), t);
|
||
|
}
|
||
|
|
||
|
template<class Op, class NData>
|
||
|
struct make_helper
|
||
|
{
|
||
|
typedef typename Derived::template type<Op, NData>::result type;
|
||
|
static type make(const NData& ndata)
|
||
|
{
|
||
|
return type(ndata);
|
||
|
}
|
||
|
};
|
||
|
};
|
||
|
|
||
|
// If your expression template is of the form
|
||
|
// template<class Operation, class Data>
|
||
|
// class my_expression ...
|
||
|
// then derived_wrapper<my_expression> is a valid argument for Derived in
|
||
|
// the expression class above.
|
||
|
template<template<class O, class D> class Derived>
|
||
|
struct derived_wrapper
|
||
|
{
|
||
|
template<class Operation, class Data>
|
||
|
struct type
|
||
|
{
|
||
|
typedef Derived<Operation, Data> result;
|
||
|
};
|
||
|
};
|
||
|
|
||
|
// If your expression template is of the form
|
||
|
// template<class Extra, class Opeartion, class Data>
|
||
|
// class my_expression2 ...
|
||
|
// where Extra is some extra information which should be passed on unchanged,
|
||
|
// then derived_wrapper2<my_expression2, Extra> is a valid argument for Derived
|
||
|
// in the expression class above.
|
||
|
template<template<class E, class O, class D> class Derived, class Extra>
|
||
|
struct derived_wrapper2
|
||
|
{
|
||
|
template<class Operation, class Data>
|
||
|
struct type
|
||
|
{
|
||
|
typedef Derived<Extra, Operation, Data> result;
|
||
|
};
|
||
|
};
|
||
|
|
||
|
|
||
|
// operators
|
||
|
|
||
|
namespace detail {
|
||
|
// These traits determine how arguments of an expression template are stored.
|
||
|
// E.g. (e1 + e2) yields a new expression template with a two-argument tuple
|
||
|
// as Data. If e1 is an immediate, then we want to (usually) store it by
|
||
|
// reference, to avoid copies. If not, we can just store by value (since
|
||
|
// copying e1 just copies the references anyway) and avoid indirection.
|
||
|
// (Similarly for e2.)
|
||
|
template<class Expr>
|
||
|
struct storage_traits
|
||
|
: mp::if_<
|
||
|
traits::is_immediate<Expr>,
|
||
|
typename traits::forwarding<Expr>::type,
|
||
|
Expr
|
||
|
> { };
|
||
|
// See tuple.h.
|
||
|
template<>
|
||
|
struct storage_traits<detail::UNUSED> {typedef detail::UNUSED type;};
|
||
|
|
||
|
template<class ev_t, class Op, class type>
|
||
|
struct nary_op_helper_step2
|
||
|
{
|
||
|
typedef typename ev_t::return_t Expr;
|
||
|
typedef typename Expr::template make_helper<Op, type> make_helper;
|
||
|
typedef typename make_helper::type return_t;
|
||
|
};
|
||
|
template<class Op, class type>
|
||
|
struct nary_op_helper_step2<rules::UNIMPLEMENTED, Op, type>
|
||
|
{
|
||
|
struct return_t { };
|
||
|
struct make_helper { };
|
||
|
};
|
||
|
|
||
|
// Helper to determine the return type of an expression, where Data is already
|
||
|
// the correct tuple type.
|
||
|
// The step1/step2 splitting above is necessary to avoid compiler errors in
|
||
|
// case there is not actually any rule.
|
||
|
template<class Op, class Data>
|
||
|
struct nary_op_helper
|
||
|
{
|
||
|
typedef typename mp::find_evaluation<Op, Data, true>::type ev_t;
|
||
|
typedef nary_op_helper_step2<ev_t, Op, Data> nohs2;
|
||
|
typedef typename nohs2::return_t return_t;
|
||
|
typedef typename nohs2::make_helper make_helper;
|
||
|
|
||
|
typedef traits::is_implemented<ev_t> cond;
|
||
|
typedef mp::enable_if<cond, return_t> enable;
|
||
|
};
|
||
|
|
||
|
template<class Op, class Maker>
|
||
|
struct nary_op_helper_maker
|
||
|
: nary_op_helper<Op, typename Maker::type>
|
||
|
{
|
||
|
typedef Maker maker;
|
||
|
};
|
||
|
|
||
|
#define FLINTXX_NARY_OP_HELPER_MACRO(arg) typename storage_traits< arg >::type
|
||
|
|
||
|
// nary_op_helper<Op, Arg1, Arg2, ...> invokes nary_op_helper with the correct
|
||
|
// tuple type as argument.
|
||
|
template<class Op, FLINTXX_MAKE_TUPLE_TEMPLATE_ARGS>
|
||
|
struct nary_op_helper2
|
||
|
: nary_op_helper_maker<Op, mp::make_tuple<
|
||
|
FLINTXX_MAKE_TUPLE_TYPES_APPLYMACRO(FLINTXX_NARY_OP_HELPER_MACRO) > >
|
||
|
{
|
||
|
typedef nary_op_helper2 noh2;
|
||
|
static typename noh2::return_t make(FLINTXX_MAKE_TUPLE_FUNC_ARGS)
|
||
|
{
|
||
|
return noh2::make_helper::make(noh2::maker::make(
|
||
|
FLINTXX_MAKE_TUPLE_FUNC_ARG_NAMES));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Special casing for binary operators.
|
||
|
template<class Expr1, class Op, class Expr2>
|
||
|
struct binary_op_helper
|
||
|
: nary_op_helper2<Op, Expr1, Expr2>
|
||
|
{ };
|
||
|
|
||
|
// Special casing for unary operations.
|
||
|
template<class Op, class Expr>
|
||
|
struct unary_op_helper : nary_op_helper2<Op, Expr> { };
|
||
|
|
||
|
// For unary member operators, determining the return type the normal way can
|
||
|
// lead to cyclic dependencies. See FLINTXX_DEFINE_MEMBER_UNOP_RTYPE.
|
||
|
template<class Ret, class Op, class Expr>
|
||
|
struct unary_op_helper_with_rettype
|
||
|
{
|
||
|
typedef mp::make_tuple<typename storage_traits<Expr>::type> maker;
|
||
|
typedef typename Ret::template make_helper<
|
||
|
Op, typename maker::type>::type return_t;
|
||
|
};
|
||
|
|
||
|
// Common helper for implementing comparison operators.
|
||
|
template<class Expr1, class Expr2>
|
||
|
struct order_op_helper
|
||
|
{
|
||
|
typedef typename tools::evaluation_helper<Expr1>::type ev1_t;
|
||
|
typedef typename tools::evaluation_helper<Expr2>::type ev2_t;
|
||
|
typedef tools::symmetric_cmp<ev1_t, ev2_t> scmp;
|
||
|
|
||
|
typedef mp::enable_if<
|
||
|
mp::and_<
|
||
|
traits::is_implemented<scmp>,
|
||
|
mp::or_<
|
||
|
traits::is_expression<Expr1>,
|
||
|
traits::is_expression<Expr2>
|
||
|
>
|
||
|
>,
|
||
|
bool> enable;
|
||
|
|
||
|
static int get(const Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
return scmp::get(tools::evaluation_helper<Expr1>::get(e1),
|
||
|
tools::evaluation_helper<Expr2>::get(e2));
|
||
|
}
|
||
|
};
|
||
|
} // detail
|
||
|
|
||
|
template<class Expr>
|
||
|
inline typename mp::enable_if<traits::is_expression<Expr>, std::ostream&>::type
|
||
|
operator<<(std::ostream& o, const Expr& e)
|
||
|
{
|
||
|
e.print(o);
|
||
|
return o;
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename mp::enable_if<traits::is_expression<Expr1>, bool>::type
|
||
|
operator==(const Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
return e1.equals(e2);
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename mp::enable_if<mp::and_<
|
||
|
mp::not_<traits::is_expression<Expr1> >,
|
||
|
traits::is_expression<Expr2> >,
|
||
|
bool>::type
|
||
|
operator==(const Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
return e2.equals(e1);
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename mp::enable_if<mp::or_<
|
||
|
traits::is_expression<Expr1>,
|
||
|
traits::is_expression<Expr2> >,
|
||
|
bool>::type
|
||
|
operator!=(const Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
return !(e1 == e2);
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename detail::order_op_helper<Expr1, Expr2>::enable::type
|
||
|
operator<(const Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
return detail::order_op_helper<Expr1, Expr2>::get(e1, e2) < 0;
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename detail::order_op_helper<Expr1, Expr2>::enable::type
|
||
|
operator<=(const Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
return detail::order_op_helper<Expr1, Expr2>::get(e1, e2) <= 0;
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename detail::order_op_helper<Expr1, Expr2>::enable::type
|
||
|
operator>(const Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
return detail::order_op_helper<Expr1, Expr2>::get(e1, e2) > 0;
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename detail::order_op_helper<Expr1, Expr2>::enable::type
|
||
|
operator>=(const Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
return detail::order_op_helper<Expr1, Expr2>::get(e1, e2) >= 0;
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename detail::binary_op_helper<
|
||
|
Expr1, operations::plus, Expr2>::enable::type
|
||
|
operator+(const Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
return detail::binary_op_helper<Expr1, operations::plus, Expr2>::make(e1, e2);
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename detail::binary_op_helper<
|
||
|
Expr1, operations::minus, Expr2>::enable::type
|
||
|
operator-(const Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
return detail::binary_op_helper<Expr1, operations::minus, Expr2>::make(e1, e2);
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename detail::binary_op_helper<
|
||
|
Expr1, operations::times, Expr2>::enable::type
|
||
|
operator*(const Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
return detail::binary_op_helper<Expr1, operations::times, Expr2>::make(e1, e2);
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename detail::binary_op_helper<
|
||
|
Expr1, operations::divided_by, Expr2>::enable::type
|
||
|
operator/(const Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
return detail::binary_op_helper<Expr1, operations::divided_by, Expr2>::make(e1, e2);
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename detail::binary_op_helper<
|
||
|
Expr1, operations::modulo, Expr2>::enable::type
|
||
|
operator%(const Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
return detail::binary_op_helper<Expr1, operations::modulo, Expr2>::make(e1, e2);
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename detail::binary_op_helper<
|
||
|
Expr1, operations::binary_and, Expr2>::enable::type
|
||
|
operator&(const Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
return detail::binary_op_helper<Expr1, operations::binary_and, Expr2>::make(e1, e2);
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename detail::binary_op_helper<
|
||
|
Expr1, operations::binary_or, Expr2>::enable::type
|
||
|
operator|(const Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
return detail::binary_op_helper<Expr1, operations::binary_or, Expr2>::make(e1, e2);
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename detail::binary_op_helper<
|
||
|
Expr1, operations::binary_xor, Expr2>::enable::type
|
||
|
operator^(const Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
return detail::binary_op_helper<Expr1, operations::binary_xor, Expr2>::make(e1, e2);
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename detail::binary_op_helper<
|
||
|
Expr1, operations::shift, Expr2>::enable::type
|
||
|
operator<<(const Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
return detail::binary_op_helper<Expr1, operations::shift, Expr2>::make(e1, e2);
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename detail::binary_op_helper<
|
||
|
Expr1, operations::shift, Expr2>::enable::type
|
||
|
operator>>(const Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
return detail::binary_op_helper<Expr1, operations::shift, Expr2>::make(e1, -e2);
|
||
|
}
|
||
|
|
||
|
template<class Expr>
|
||
|
inline typename detail::unary_op_helper<operations::negate, Expr>::enable::type
|
||
|
operator-(const Expr& e)
|
||
|
{
|
||
|
return detail::unary_op_helper<operations::negate, Expr>::make(e);
|
||
|
}
|
||
|
|
||
|
template<class Expr>
|
||
|
inline typename detail::unary_op_helper<operations::complement, Expr>::enable::type
|
||
|
operator~(const Expr& e)
|
||
|
{
|
||
|
return detail::unary_op_helper<operations::complement, Expr>::make(e);
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename mp::enable_if<traits::is_immediate_expr<Expr1>, Expr1&>::type
|
||
|
operator+=(Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
e1.set(e1 + e2);
|
||
|
return e1;
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename mp::enable_if<traits::is_immediate_expr<Expr1>, Expr1&>::type
|
||
|
operator-=(Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
e1.set(e1 - e2);
|
||
|
return e1;
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename mp::enable_if<traits::is_immediate_expr<Expr1>, Expr1&>::type
|
||
|
operator*=(Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
e1.set(e1 * e2);
|
||
|
return e1;
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename mp::enable_if<traits::is_immediate_expr<Expr1>, Expr1&>::type
|
||
|
operator/=(Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
e1.set(e1 / e2);
|
||
|
return e1;
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename mp::enable_if<traits::is_immediate_expr<Expr1>, Expr1&>::type
|
||
|
operator%=(Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
e1.set(e1 % e2);
|
||
|
return e1;
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename mp::enable_if<traits::is_immediate_expr<Expr1>, Expr1&>::type
|
||
|
operator|=(Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
e1.set(e1 | e2);
|
||
|
return e1;
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename mp::enable_if<traits::is_immediate_expr<Expr1>, Expr1&>::type
|
||
|
operator&=(Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
e1.set(e1 & e2);
|
||
|
return e1;
|
||
|
}
|
||
|
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename mp::enable_if<traits::is_immediate_expr<Expr1>, Expr1&>::type
|
||
|
operator^=(Expr1& e1, const Expr2& e2)
|
||
|
{
|
||
|
e1.set(e1 ^ e2);
|
||
|
return e1;
|
||
|
}
|
||
|
|
||
|
// c-style IO
|
||
|
template<class T>
|
||
|
typename mp::enable_if<traits::is_implemented<
|
||
|
rules::cprint<typename T::evaluated_t> >, int>::type
|
||
|
print(const T& t)
|
||
|
{
|
||
|
return t.print();
|
||
|
}
|
||
|
template<class T>
|
||
|
typename mp::enable_if<traits::is_implemented<
|
||
|
rules::cprint<typename T::evaluated_t> >, int>::type
|
||
|
print(FILE* f, const T& t)
|
||
|
{
|
||
|
return t.print(f);
|
||
|
}
|
||
|
template<class T, class U>
|
||
|
typename mp::enable_if<traits::is_implemented<
|
||
|
rules::print_pretty<typename T::evaluated_t> >, int>::type
|
||
|
print_pretty(const T& t, const U& extra)
|
||
|
{
|
||
|
return t.print_pretty(extra);
|
||
|
}
|
||
|
template<class T, class U>
|
||
|
typename mp::enable_if<traits::is_implemented<
|
||
|
rules::print_pretty<typename T::evaluated_t> >, int>::type
|
||
|
print_pretty(FILE* f, const T& t, const U& extra)
|
||
|
{
|
||
|
return t.print_pretty(extra, f);
|
||
|
}
|
||
|
template<class T>
|
||
|
typename mp::enable_if<traits::is_implemented<
|
||
|
rules::print_pretty<typename T::evaluated_t> >, int>::type
|
||
|
print_pretty(const T& t)
|
||
|
{
|
||
|
return t.print_pretty();
|
||
|
}
|
||
|
template<class T>
|
||
|
typename mp::enable_if<traits::is_implemented<
|
||
|
rules::print_pretty<typename T::evaluated_t> >, int>::type
|
||
|
print_pretty(FILE* f, const T& t)
|
||
|
{
|
||
|
return t.print_pretty(f);
|
||
|
}
|
||
|
template<class T>
|
||
|
typename mp::enable_if<traits::is_implemented<
|
||
|
rules::read<typename T::evaluated_t> >, int>::type
|
||
|
read(T& t)
|
||
|
{
|
||
|
return t.read();
|
||
|
}
|
||
|
template<class T>
|
||
|
typename mp::enable_if<traits::is_implemented<
|
||
|
rules::read<typename T::evaluated_t> >, int>::type
|
||
|
read(FILE* f, T& t)
|
||
|
{
|
||
|
return t.read(f);
|
||
|
}
|
||
|
|
||
|
// TODO move to std?
|
||
|
template<class Expr1, class Expr2>
|
||
|
inline typename mp::enable_if<typename traits::is_implemented<
|
||
|
rules::swap<Expr1, Expr2> > >::type swap(Expr1& e1, Expr2& e2)
|
||
|
{
|
||
|
rules::swap<Expr1, Expr2>::doit(e1, e2);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// TODO remove this?
|
||
|
#include "default_rules.h"
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
// HELPER MACROS
|
||
|
////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
// To be called in any namespace
|
||
|
|
||
|
// Make the binary operation "name" available in current namespace
|
||
|
#define FLINT_DEFINE_BINOP_HERE(name) \
|
||
|
template<class T1, class T2> \
|
||
|
inline typename ::flint::detail::binary_op_helper<\
|
||
|
T1, ::flint::operations::name##_op, T2>::enable::type \
|
||
|
name(const T1& t1, const T2& t2) \
|
||
|
{ \
|
||
|
return ::flint::detail::binary_op_helper< \
|
||
|
T1, ::flint::operations::name##_op, T2>::make(t1, t2); \
|
||
|
}
|
||
|
|
||
|
// Make the unary operation "name" available in current namespace
|
||
|
#define FLINT_DEFINE_UNOP_HERE(name) \
|
||
|
template<class T1> \
|
||
|
inline typename ::flint::detail::unary_op_helper<\
|
||
|
::flint::operations::name##_op, T1>::enable::type \
|
||
|
name(const T1& t1) \
|
||
|
{ \
|
||
|
return ::flint::detail::unary_op_helper< ::flint::operations::name##_op, T1>::make(t1); \
|
||
|
}
|
||
|
|
||
|
// Make the threeary operation "name" available in current namespace
|
||
|
#define FLINT_DEFINE_THREEARY_HERE(name) \
|
||
|
template<class T1, class T2, class T3> \
|
||
|
inline typename ::flint::detail::nary_op_helper2<\
|
||
|
::flint::operations::name##_op, T1, T2, T3>::enable::type \
|
||
|
name(const T1& t1, const T2& t2, const T3& t3) \
|
||
|
{ \
|
||
|
return ::flint::detail::nary_op_helper2< \
|
||
|
::flint::operations::name##_op, T1, T2, T3>::make(t1, t2, t3); \
|
||
|
}
|
||
|
|
||
|
// Make the threeary operation "name" available in current namespace,
|
||
|
// but with only two arguments, the second of which is of type type1 and
|
||
|
// defaults to val1, and the third argument always (implicitly) of type type2
|
||
|
// and value val2.
|
||
|
// The suggested usage of this macro is to first call FLINT_DEFINE_THREEARY_HERE,
|
||
|
// and then call FLINT_DEFINE_THREEARY_HERE_2DEFAULT. The effect will be an
|
||
|
// operation which can be invoked with 1, 2 or 3 arguments.
|
||
|
#define FLINT_DEFINE_THREEARY_HERE_2DEFAULT(name, type1, val1, type2, val2) \
|
||
|
template<class T1> \
|
||
|
inline typename ::flint::detail::nary_op_helper2<\
|
||
|
::flint::operations::name##_op, T1, type1, type2 >::enable::type \
|
||
|
name(const T1& t1, type1 t2 = val1) \
|
||
|
{ \
|
||
|
return ::flint::detail::nary_op_helper2< \
|
||
|
::flint::operations::name##_op, T1, type1, type2>::make(t1, t2, val2); \
|
||
|
}
|
||
|
|
||
|
// Make the fourary operation "name" available in current namespace
|
||
|
#define FLINT_DEFINE_FOURARY_HERE(name) \
|
||
|
template<class T1, class T2, class T3, class T4> \
|
||
|
inline typename ::flint::detail::nary_op_helper2<\
|
||
|
::flint::operations::name##_op, T1, T2, T3, T4>::enable::type \
|
||
|
name(const T1& t1, const T2& t2, const T3& t3, const T4& t4) \
|
||
|
{ \
|
||
|
return ::flint::detail::nary_op_helper2< \
|
||
|
::flint::operations::name##_op, T1, T2, T3, T4>::make(t1, t2, t3, t4); \
|
||
|
}
|
||
|
|
||
|
// Make the fiveary operation "name" available in current namespace
|
||
|
#define FLINT_DEFINE_FIVEARY_HERE(name) \
|
||
|
template<class T1, class T2, class T3, class T4, class T5> \
|
||
|
inline typename ::flint::detail::nary_op_helper2<\
|
||
|
::flint::operations::name##_op, T1, T2, T3, T4, T5>::enable::type \
|
||
|
name(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5) \
|
||
|
{ \
|
||
|
return ::flint::detail::nary_op_helper2< \
|
||
|
::flint::operations::name##_op, T1, T2, T3, T4, T5>::make(t1, t2, t3, t4, t5); \
|
||
|
}
|
||
|
|
||
|
// Make the sixary operation "name" available in current namespace
|
||
|
#define FLINT_DEFINE_SIXARY_HERE(name) \
|
||
|
template<class T1, class T2, class T3, class T4, class T5, class T6> \
|
||
|
inline typename ::flint::detail::nary_op_helper2<\
|
||
|
::flint::operations::name##_op, T1, T2, T3, T4, T5, T6>::enable::type \
|
||
|
name(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5, const T6& t6) \
|
||
|
{ \
|
||
|
return ::flint::detail::nary_op_helper2< \
|
||
|
::flint::operations::name##_op, T1, T2, T3, T4, T5, T6>::make(t1, t2, t3, t4, t5, t6); \
|
||
|
}
|
||
|
|
||
|
// Make the sevenary operation "name" available in current namespace
|
||
|
#define FLINT_DEFINE_SEVENARY_HERE(name) \
|
||
|
template<class T1, class T2, class T3, class T4, class T5, class T6, class T7> \
|
||
|
inline typename ::flint::detail::nary_op_helper2<\
|
||
|
::flint::operations::name##_op, T1, T2, T3, T4, T5, T6, T7>::enable::type \
|
||
|
name(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5, const T6& t6, const T7& t7) \
|
||
|
{ \
|
||
|
return ::flint::detail::nary_op_helper2< \
|
||
|
::flint::operations::name##_op, T1, T2, T3, T4, T5, T6, T7>::make(t1, t2, t3, t4, t5, t6, t7); \
|
||
|
}
|
||
|
|
||
|
// This set of macros should be called in namespace flint.
|
||
|
|
||
|
// Introduce a new binary operation called "name"
|
||
|
// NB: because of ADL bugs in g++ <= 4.4, the operation tag is called "name_op",
|
||
|
// whereas the function corresponding to it is just called "name"
|
||
|
#define FLINT_DEFINE_BINOP(name) \
|
||
|
namespace operations { \
|
||
|
struct name##_op { }; \
|
||
|
} \
|
||
|
FLINT_DEFINE_BINOP_HERE(name)
|
||
|
|
||
|
// This macro can be used to conditionally enable a function, and is mostly
|
||
|
// used for forwarding.
|
||
|
// A typical usage is
|
||
|
// template<class T, class U>
|
||
|
// FLINT_BINOP_ENABLE_RETTYPE(myop, T, U) myop_other(const T& t, const U& u)
|
||
|
// {
|
||
|
// // perhaps something more interesting
|
||
|
// return myop(t, u);
|
||
|
// }
|
||
|
#define FLINT_BINOP_ENABLE_RETTYPE(name, T1, T2) \
|
||
|
typename detail::binary_op_helper<T1, operations::name##_op, T2>::enable::type
|
||
|
|
||
|
// Introduce a new unary operation called "name"
|
||
|
#define FLINT_DEFINE_UNOP(name) \
|
||
|
namespace operations { \
|
||
|
struct name##_op { }; \
|
||
|
} \
|
||
|
FLINT_DEFINE_UNOP_HERE(name)
|
||
|
|
||
|
#define FLINT_UNOP_ENABLE_RETTYPE(name, T) \
|
||
|
typename detail::unary_op_helper<operations::name##_op, T>::return_t
|
||
|
|
||
|
// See FLINTXX_DEFINE_MEMBER_UNOP_RTYPE
|
||
|
#define FLINT_UNOP_BUILD_RETTYPE(name, rettype, T) \
|
||
|
typename detail::unary_op_helper_with_rettype<rettype, \
|
||
|
operations::name##_op, T>::return_t
|
||
|
|
||
|
#define FLINT_DEFINE_THREEARY(name) \
|
||
|
namespace operations { \
|
||
|
struct name##_op { }; \
|
||
|
} \
|
||
|
FLINT_DEFINE_THREEARY_HERE(name)
|
||
|
|
||
|
#define FLINT_THREEARY_ENABLE_RETTYPE(name, T1, T2, T3) \
|
||
|
typename detail::nary_op_helper2<operations::name##_op, T1, T2, T3>::enable::type
|
||
|
|
||
|
#define FLINT_DEFINE_FOURARY(name) \
|
||
|
namespace operations { \
|
||
|
struct name##_op { }; \
|
||
|
} \
|
||
|
FLINT_DEFINE_FOURARY_HERE(name)
|
||
|
|
||
|
#define FLINT_FOURARY_ENABLE_RETTYPE(name, T1, T2, T3, T4) \
|
||
|
typename detail::nary_op_helper2<operations::name##_op, T1, T2, T3, T4>::enable::type
|
||
|
|
||
|
#define FLINT_DEFINE_FIVEARY(name) \
|
||
|
namespace operations { \
|
||
|
struct name##_op { }; \
|
||
|
} \
|
||
|
FLINT_DEFINE_FIVEARY_HERE(name)
|
||
|
|
||
|
#define FLINT_FIVEARY_ENABLE_RETTYPE(name, T1, T2, T3, T4, T5) \
|
||
|
typename detail::nary_op_helper2<operations::name##_op, T1, T2, T3, T4, T5>::enable::type
|
||
|
|
||
|
#define FLINT_DEFINE_SIXARY(name) \
|
||
|
namespace operations { \
|
||
|
struct name##_op { }; \
|
||
|
} \
|
||
|
FLINT_DEFINE_SIXARY_HERE(name)
|
||
|
|
||
|
#define FLINT_DEFINE_SEVENARY(name) \
|
||
|
namespace operations { \
|
||
|
struct name##_op { }; \
|
||
|
} \
|
||
|
FLINT_DEFINE_SEVENARY_HERE(name)
|
||
|
|
||
|
#endif
|