#pragma once #include #include #include #include #include #include namespace AMDiS { namespace Operation { /// \brief Composition of Functors. /** * \ingroup operations * Implement the composition `c:=f(g1,g2,...,gN), where `f` is an N-ary functor * `gI` are functors of the same arity. The composition is defined as * ``` * c(a1,a2,...,aM) := f(g1(a1,a2,..,aM), g2(a1,a2,...,aM),...,gN(a1,a2,...,aM)) * ``` * * **Requirements:** * - sizeof...(Gs) == N, with N = arity(F) * - arity(Gs[0]) == arity(Gs[i]) for i = 1,...,N-1 **/ template struct Composer { template , Types>) > constexpr Composer(F_&& f, Gs_&&... gs) : f_(std::forward(f)) , gs_(std::forward(gs)...) {} template constexpr auto operator()(Ts const&... args) const { auto eval = [&](auto const& g) { return g(args...); }; return Dune::Std::apply([&,this](auto const&... gs) { return f_(eval(gs)...); }, gs_); } F f_; std::tuple gs_; }; #ifndef DOXYGEN template struct ComposerBuilder { using type = Composer; template static constexpr type build(F_&& f, Gs_&&... gs) { return type{std::forward(f), std::forward(gs)...}; } }; #endif /// Generator function for \ref Composer template constexpr auto compose(F&& f, Gs&&... gs) { return ComposerBuilder, std::decay_t...>::build( std::forward(f), std::forward(gs)...); } // Polynomial order or composed function combines the orders of the sub-functions template && all_of_v...>)> int order(Composer const& c, Int... degrees) { auto deg = [&](auto const& g) { return order(g, int(degrees)...); }; return Dune::Std::apply([&](auto const&... gs) { return order(c.f_, deg(gs)...); }, c.gs_); } /// Partial derivative of composed function: /// Implements: // sum_i [ d_i(f)[g...] * d_j(g_i) ] template auto partial(Composer const& c, index_t _j); #ifndef DOXYGEN // some specialization for the composer // id(g) => g template struct ComposerBuilder { using type = F; template static constexpr F build(F_&& f) { return F{std::forward(f)}; } }; #endif } // end namespace Operation } // end namespace AMDiS