TreeContainer.hpp 10.2 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
#pragma once

#include <array>
#include <functional>
#include <type_traits>
#include <utility>

#include <dune/common/indices.hh>
#include <dune/common/tuplevector.hh>

#include <dune/typetree/treepath.hh>

#include <amdis/common/Apply.hpp>
Praetorius, Simon's avatar
Praetorius, Simon committed
14
#include <amdis/common/TypeTraits.hpp>
15
#include <amdis/typetree/TreePath.hpp>
16
17
18

// NOTE: backport of dune/typetree/treecontainer.hh

19
20
21
22
23
namespace AMDiS {
namespace TypeTree {

template <class Value>
class LeafNodeStorage
24
{
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public:
  template <class V>
  LeafNodeStorage(V&& value)
    : value_(FWD(value))
  {}

  LeafNodeStorage()
    : value_()
  {}

  Value& value() { return value_; }
  Value const& value() const { return value_; }

  bool operator==(LeafNodeStorage const& other) const
39
  {
40
41
    return value_ == other.value_;
  }
42

43
44
45
private:
  Value value_;
};
46

47
48
49
template <class Value>
LeafNodeStorage(Value const&)
  -> LeafNodeStorage<Value>;
50
51


52
53
54
55
56
57
58
59
60
template <class Value, class Container>
class InnerNodeStorage
{
public:
  template <class V, class C>
  InnerNodeStorage(V&& value, C&& container)
    : value_(FWD(value))
    , container_(FWD(container))
  {}
61

62
63
64
65
  InnerNodeStorage()
    : value_()
    , container_()
  {}
66

67
68
  template <class I>
  auto& operator[](I const& i) { return container_[i]; }
69

70
71
  template <class I>
  auto const& operator[](I const& i) const { return container_[i]; }
72

73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
  Value& value() { return value_; }
  Value const& value() const { return value_; }

  Container& container() { return container_; }
  Container const& container() const { return container_; }

  bool operator==(InnerNodeStorage const& other) const
  {
    return value_ == other.value_ && container_ == other.container_;
  }

private:
  Value value_;
  Container container_;
};

template <class Value, class Container>
InnerNodeStorage(Value const&, Container const&)
  -> InnerNodeStorage<Value, Container>;


/// \brief A factory class creating a hybrid container compatible with a type tree
/**
 * This class allows to create a nested hybrid container having the same structure
 * as a given type tree. Power nodes are represented as std::array's while composite
 * nodes are represented as Dune::TupleVector's. The stored values for the leaf nodes
 * are creating using a given predicate. Once created, the factory provides an
 * operator() creating the container for the tree given as argument.
 *
 * \tparam NodeToValue Type of a predicate that determines the stored values at the
 *                     leafs
 **/
template <class NodeToValue, bool leafOnly = false>
class ContainerFactory
{
public:
  /// \brief Create ContainerFactory
  /**
    * The given predicate will be stored by value.
    *
    * \param A predicate used to generate the stored values for the leaves
    */
  ContainerFactory(NodeToValue nodeToValue)
    : nodeToValue_(std::move(nodeToValue))
  {}

  /// \brief Return a container for storing the node content
  template <class Node>
  auto operator()(Node const& node) const
  {
    if constexpr (Node::isLeaf)
      return LeafNodeStorage{nodeToValue_(node)};
    else
    if constexpr (Node::isPower) {
      using TransformedChild = decltype((*this)(node.child(0)));
      return makeInnerNodeStorage(node,
        std::array<TransformedChild, Node::degree()>());
    }
    else
    if constexpr (Node::isComposite) {
      return makeInnerNodeStorage(node,
        Ranges::applyIndices<Node::degree()>(
        [&](auto... ii) { return Dune::makeTupleVector((*this)(node.child(ii))...); }));
136
    }
137
138
139
140
141
142
    else {
      static_assert(Node::isLeaf || Node::isPower || Node::isComposite,
        "Node must be one of leaf, power, composite.");
      return;
    }
  }
143

144
145
146
147
148
149
150
151
  template <class Node, class Container>
  auto makeInnerNodeStorage(Node const& node, Container&& container) const
  {
    if constexpr(!Node::isLeaf && leafOnly)
      return FWD(container);
    else
      return InnerNodeStorage{nodeToValue_(node), FWD(container)};
  }
152

153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
private:
  NodeToValue nodeToValue_;
};

/// \brief Vector data-structure with tree-path index access and hierarchic structure
/// given by the `Container` template type
/**
 * This Vector container is parametrized with the actual container type that is stored
 * internally. Access to the elements of the container is possible by using a tree-path
 * index.
 *
 * The internal container is constructed by the \ref ContainerFactory, storing for each
 * tree node a corresponding array or tuple plus a value. The data-structure to hold
 * both, the value and the container is defined in \ref InnerNodeStorage.
 **/
template <class Container>
class TreeContainerStorage
{
  using Self = TreeContainerStorage;
172

173
174
175
  template <class C>
  static constexpr auto
  accessByTreePath(C&& container, Dune::TypeTree::HybridTreePath<>) -> decltype(container.value())
176
  {
177
178
179
180
181
182
    return container.value();
  }

  template <class C, class T0, class... T>
  static constexpr decltype(auto)
  accessByTreePath(C&& container, Dune::TypeTree::HybridTreePath<T0,T...> const& path)
183
  {
184
185
    auto head = Dune::TypeTree::treePathEntry(path,Dune::Indices::_0);
    return accessByTreePath(container[head], pop_front(path));
186
187
  }

188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
public:
  /// \brief Default-construct the tree-container
  TreeContainerStorage()
    : container_()
  {}

  /// \brief Construct the tree-container from a given container storage
  TreeContainerStorage(Container const& container)
    : container_(container)
  {}

  /// \brief Construct the tree-container from a given container storage
  TreeContainerStorage(Container&& container)
    : container_(std::move(container))
  {}

  /// \brief Access a (const) element of the container by treepath
  template <class... T>
  decltype(auto) operator[](Dune::TypeTree::HybridTreePath<T...> const& path) const
  {
    return accessByTreePath(container_, path);
  }
210

211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
  /// \brief Access a (mutable) element of the container by treepath
  template <class... T>
  decltype(auto) operator[](Dune::TypeTree::HybridTreePath<T...> const& path)
  {
    return accessByTreePath(container_, path);
  }

  /// \brief Obtain the container (const)
  Container const& data() const
  {
    return container_;
  }

  /// \brief Obtain the container (mutable)
  Container& data()
  {
    return container_;
  }

  /// \brief Compare two containers for equality
  bool operator==(TreeContainerStorage const& other) const
232
  {
233
    return container_ == other.container_;
234
  }
235

236
237
  /// \brief Compare two containers for inequality
  bool operator!=(TreeContainerStorage const& other) const
238
  {
239
    return container_ != other.container_;
240
  }
241

242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
private:
  Container container_;
};



/// \brief Create container having the same structure as the given tree
/**
 * This class allows to create a nested hybrid container having the same structure
 * as a given type tree. Power nodes are represented as std::array's while composite
 * nodes are represented as Dune::TupleVector's. The stored values for the leaf nodes
 * are creating using a given predicate. For convenience the created container is
 * not returned directly. Instead, the returned object stores the container and
 * provides operator[] access using a HybridTreePath.
 *
 * \tparam leafOnly   Create a container for the leaf tree nodes
 * \param tree        The tree which should be mapper to a container
 * \param nodeToValue A predicate used to generate the stored values for the nodes
 *
 * \returns A container matching the tree structure
 */
template <bool leafOnly = false, class Tree, class NodeToValue>
auto treeContainer(Tree const& tree, NodeToValue nodeToValue)
{
  auto factory = ContainerFactory<NodeToValue,leafOnly>{nodeToValue};
  return TreeContainerStorage{factory(tree)};
}


/// \brief Create container having the same structure as the given tree
/**
 * This class allows to create a nested hybrid container having the same structure
 * as a given type tree. Power nodes are represented as std::array's while composite
 * nodes are represented as Dune::TupleVector's. The stored values for the leaf nodes
 * are of the given type Value. For convenience the created container is
 * not returned directly. Instead, the returned object stores the container and
 * provides operator[] access using a HybridTreePath.
 *
 * \tparam Value      Type of the values to be stored for the leafs. Should be default
 *                    constructible.
 * \param nodeToValue A predicate used to generate the stored values for the nodes
 *
 * \returns           A container matching the tree structure
 */
template <class Value, bool leafOnly = false, class Tree>
auto treeContainer(Tree const& tree)
{
  return treeContainer<leafOnly>(tree, [](auto&&) { return Value{}; });
}

template <template<class> class NodeData, bool leafOnly = false, class Tree>
auto treeContainer(Tree const& tree)
{
  return treeContainer<leafOnly>(tree, [](auto&& node) { return NodeData<TYPEOF(node)>{}; });
}

/// Alias to container type generated by treeContainer for given tree type and
/// uniform value type
template <class Value, class Tree, bool leafOnly = false>
using UniformTreeContainer
  = TYPEOF(treeContainer<Value,leafOnly>(std::declval<const Tree&>()));
303

304
305
306
307
308
/// Alias to matrix-container type generated by treeContainer for given tree types
/// and uniform value type
template <class Value, class RowTree, class ColTree = RowTree, bool leafOnly = false>
using UniformTreeMatrix
  = UniformTreeContainer<UniformTreeContainer<Value,ColTree,leafOnly>,RowTree,leafOnly>;
309

310
311
312
313
314
/// Alias to container type generated by treeContainer for give tree type and when
/// using NodeToValue to create values
template <template <class Node> class NodeData, class Tree, bool leafOnly = false>
using TreeContainer
  = TYPEOF(treeContainer<NodeData,leafOnly>(std::declval<const Tree&>()));
315
316


317
318
319
320
321
322
323
namespace Impl_ {

template <template <class,class> class NodeData, class Tree, bool leafOnly>
struct RowNodeData
{
  template <class RowNode>
  struct ColNodeData
324
  {
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
    template <class ColNode>
    using type = NodeData<RowNode, ColNode>;
  };

  template <class RowNode>
  using type = TreeContainer<ColNodeData<RowNode>::template type, Tree, leafOnly>;
};

} // end namespace Impl_

/// Alias to matrix-container type generated by treeContainer for give tree type
/// and when using NodeToValue to create values
template <template <class,class> class NodeData, class RowTree, class ColTree = RowTree, bool leafOnly = false>
using TreeMatrix
  = TreeContainer<Impl_::RowNodeData<NodeData,ColTree,leafOnly>::template type,RowTree,leafOnly>;

}} //namespace AMDiS::TypeTree