piranha  0.10
substitutable_series.hpp
1 /* Copyright 2009-2017 Francesco Biscani (bluescarni@gmail.com)
2 
3 This file is part of the Piranha library.
4 
5 The Piranha library is free software; you can redistribute it and/or modify
6 it under the terms of either:
7 
8  * the GNU Lesser General Public License as published by the Free
9  Software Foundation; either version 3 of the License, or (at your
10  option) any later version.
11 
12 or
13 
14  * the GNU General Public License as published by the Free Software
15  Foundation; either version 3 of the License, or (at your option) any
16  later version.
17 
18 or both in parallel, as here.
19 
20 The Piranha library is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 for more details.
24 
25 You should have received copies of the GNU General Public License and the
26 GNU Lesser General Public License along with the Piranha library. If not,
27 see https://www.gnu.org/licenses/. */
28 
29 #ifndef PIRANHA_SUBSTITUTABLE_SERIES_HPP
30 #define PIRANHA_SUBSTITUTABLE_SERIES_HPP
31 
32 #include <string>
33 #include <type_traits>
34 #include <utility>
35 
36 #include <piranha/forwarding.hpp>
37 #include <piranha/math.hpp>
38 #include <piranha/series.hpp>
39 #include <piranha/symbol_utils.hpp>
40 #include <piranha/type_traits.hpp>
41 
42 namespace piranha
43 {
44 
45 namespace detail
46 {
47 
48 // Tag for the subs toolbox.
49 struct substitutable_series_tag {
50 };
51 }
52 
54 
75 template <typename Series, typename Derived>
76 class substitutable_series : public Series, detail::substitutable_series_tag
77 {
78  typedef Series base;
79  // Detect subs term.
80  template <typename Term, typename T>
81  using subs_term_score
82  = std::integral_constant<unsigned,
83  static_cast<unsigned>(has_subs<typename Term::cf_type, T>::value)
84  + (static_cast<unsigned>(key_has_subs<typename Term::key_type, T>::value) << 1u)>;
85  // Case 1: subs only on cf.
86  template <typename T, typename Term>
87  using cf_subs_type
88  = decltype(math::subs(std::declval<typename Term::cf_type const &>(), std::declval<const symbol_fmap<T> &>()));
89  template <typename T, typename Term>
90  using ret_type_1 = decltype(std::declval<const cf_subs_type<T, Term> &>() * std::declval<Derived const &>());
91  template <typename T, typename Term, enable_if_t<subs_term_score<Term, T>::value == 1u, int> = 0>
92  static ret_type_1<T, Term> subs_term_impl(const Term &t, const symbol_fmap<T> &dict, const symbol_idx_fmap<T> &,
93  const symbol_fset &s_set)
94  {
95  // NOTE: in these functions we could probably have optimised implementations, like in series'
96  // partial, that use term insertion instead of arithmetics if the types allow it.
97  Derived tmp;
98  tmp.set_symbol_set(s_set);
99  tmp.insert(Term(typename Term::cf_type(1), t.m_key));
100  // NOTE: use moves here in case the multiplication can take advantage.
101  return math::subs(t.m_cf, dict) * std::move(tmp);
102  }
103  // Case 2: subs only on key.
104  template <typename T, typename Term>
105  using k_subs_type = typename decltype(std::declval<const typename Term::key_type &>().subs(
106  std::declval<const symbol_idx_fmap<T> &>(), std::declval<const symbol_fset &>()))::value_type::first_type;
107  template <typename T, typename Term>
108  using ret_type_2_ = decltype(std::declval<Derived const &>() * std::declval<const k_subs_type<T, Term> &>());
109  template <typename T, typename Term>
110  using ret_type_2 = enable_if_t<conjunction<is_addable_in_place<ret_type_2_<T, Term>>,
111  std::is_constructible<ret_type_2_<T, Term>, const int &>>::value,
112  ret_type_2_<T, Term>>;
113  template <typename T, typename Term, enable_if_t<subs_term_score<Term, T>::value == 2u, int> = 0>
114  static ret_type_2<T, Term> subs_term_impl(const Term &t, const symbol_fmap<T> &, const symbol_idx_fmap<T> &idx,
115  const symbol_fset &s_set)
116  {
117  ret_type_2<T, Term> retval(0);
118  auto ksubs = t.m_key.subs(idx, s_set);
119  for (auto &p : ksubs) {
120  Derived tmp;
121  tmp.set_symbol_set(s_set);
122  tmp.insert(Term{t.m_cf, std::move(p.second)});
123  // NOTE: possible use of multadd here in the future.
124  retval += std::move(tmp) * std::move(p.first);
125  }
126  return retval;
127  }
128  // Case 3: subs on cf and key.
129  // NOTE: the checks on type 2 are already present in the alias above.
130  template <typename T, typename Term>
131  using ret_type_3
132  = decltype(std::declval<const cf_subs_type<T, Term> &>() * std::declval<const ret_type_2<T, Term> &>());
133  template <typename T, typename Term, enable_if_t<subs_term_score<Term, T>::value == 3u, int> = 0>
134  static ret_type_3<T, Term> subs_term_impl(const Term &t, const symbol_fmap<T> &dict, const symbol_idx_fmap<T> &idx,
135  const symbol_fset &s_set)
136  {
137  // Accumulator for the sum below. This is the same type resulting from case 2.
138  ret_type_2<T, Term> acc(0);
139  auto ksubs = t.m_key.subs(idx, s_set);
140  auto cf_subs = math::subs(t.m_cf, dict);
141  for (auto &p : ksubs) {
142  Derived tmp;
143  tmp.set_symbol_set(s_set);
144  tmp.insert(Term(typename Term::cf_type(1), std::move(p.second)));
145  // NOTE: multadd chance.
146  acc += std::move(tmp) * std::move(p.first);
147  }
148  return std::move(cf_subs) * std::move(acc);
149  }
150  // Initial definition of the subs type.
151  template <typename T>
152  using subs_type_ = decltype(
153  subs_term_impl(std::declval<typename Series::term_type const &>(), std::declval<const symbol_fmap<T> &>(),
154  std::declval<const symbol_idx_fmap<T> &>(), std::declval<symbol_fset const &>()));
155  // Enable conditionally based on the common requirements in the subs() method, plus on the fact that
156  // the subs type must be returnable.
157  // NOTE: the returnable check here should be enough, as the functions above will not be called if the check
158  // here is not passed.
159  template <typename T>
160  using subs_type
161  = enable_if_t<conjunction<std::is_constructible<subs_type_<T>, const int &>, is_addable_in_place<subs_type_<T>>,
162  is_returnable<subs_type_<T>>, has_sm_intersect_idx<T>>::value,
163  subs_type_<T>>;
164 
165 public:
167  substitutable_series() = default;
169  substitutable_series(const substitutable_series &) = default;
175  {
177  PIRANHA_TT_CHECK(is_series, Derived);
178  PIRANHA_TT_CHECK(std::is_base_of, substitutable_series, Derived);
179  }
181 
188  substitutable_series &operator=(const substitutable_series &other) = default;
190 
198 
217  template <typename T>
218  subs_type<T> subs(const symbol_fmap<T> &dict) const
219  {
220  const auto idx = sm_intersect_idx(this->m_symbol_set, dict);
221  subs_type<T> retval(0);
222  for (const auto &t : this->m_container) {
223  retval += subs_term_impl(t, dict, idx, this->m_symbol_set);
224  }
225  return retval;
226  }
227 };
228 
229 namespace detail
230 {
231 
232 // Enabler for the specialisation of subs functor for subs series.
233 template <typename Series, typename T>
234 using subs_impl_subs_series_enabler = enable_if_t<conjunction<
235  std::is_base_of<substitutable_series_tag, Series>,
236  is_returnable<decltype(std::declval<const Series &>().subs(std::declval<const symbol_fmap<T> &>()))>>::value>;
237 }
238 
239 namespace math
240 {
241 
243 
247 template <typename Series, typename T>
248 struct subs_impl<Series, T, detail::subs_impl_subs_series_enabler<Series, T>> {
250 
260  auto operator()(const Series &s, const symbol_fmap<T> &dict) const -> decltype(s.subs(dict))
261  {
262  return s.subs(dict);
263  }
264 };
265 }
266 }
267 
268 #endif
substitutable_series()=default
Defaulted default constructor.
boost::container::flat_map< std::string, T > symbol_fmap
Flat map of symbols.
In-place addable type trait.
Type trait to detect the presence of the piranha::math::subs function.
Definition: math.hpp:2254
#define PIRANHA_FORWARDING_CTOR(Derived, Base)
Constructor-forwarding macro.
Definition: forwarding.hpp:50
Forwarding macros.
~substitutable_series()
Trivial destructor.
boost::container::flat_set< std::string > symbol_fset
Flat set of symbols.
symbol_idx_fmap< T > sm_intersect_idx(const symbol_fset &s, const symbol_fmap< T > &m)
Find the indices of the intersection of a symbol_fset and a symbol_fmap.
Type trait to detect the presence of the substitution method in keys.
Definition: math.hpp:2360
Detect if type can be returned from a function.
Toolbox for substitutable series.
boost::container::flat_map< symbol_idx, T > symbol_idx_fmap
Flat map of symbol indices.
Root piranha namespace.
Definition: array_key.hpp:52
Type traits.
subs_type< T > subs(const symbol_fmap< T > &dict) const
Substitution.
#define PIRANHA_TT_CHECK(tt,...)
Macro for static type trait checks.
Default functor for the implementation of piranha::math::subs().
Definition: math.hpp:842
Type trait to detect series types.
Definition: series_fwd.hpp:49
detail::math_subs_type< T, U > subs(const T &x, const symbol_fmap< U > &dict)
Substitution.
Definition: math.hpp:881
#define PIRANHA_FORWARDING_ASSIGNMENT(Derived, Base)
Assignment-forwarding macro.
Definition: forwarding.hpp:68
auto operator()(const Series &s, const symbol_fmap< T > &dict) const -> decltype(s.subs(dict))
Call operator.
substitutable_series & operator=(const substitutable_series &other)=default
Copy assignment operator.