1 /*
2   HMat-OSS (HMatrix library, open source software)
3 
4   Copyright (C) 2014-2015 Airbus Group SAS
5 
6   This program is free software; you can redistribute it and/or
7   modify it under the terms of the GNU General Public License
8   as published by the Free Software Foundation; either version 2
9   of the License, or (at your option) any later version.
10 
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15 
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 
20   http://github.com/jeromerobert/hmat-oss
21 */
22 
23 /*! \file
24   \ingroup HMatrix
25   \brief Templated Tree class used by ClusterTree and HMatrix.
26 */
27 #ifndef _TREE_HPP
28 #define _TREE_HPP
29 #include <vector>
30 #include <list>
31 #include <cstddef>
32 #include <assert.h>
33 
34 namespace hmat {
35 
36 // Forward declaration
37 template <typename TreeNode> class Tree;
38 
39 /* Visitor pattern
40  */
41 enum Visit { tree_preorder, tree_postorder, tree_inorder, tree_leaf };
42 
43 /** Class to recursively apply a given function to all nodes of a tree
44  */
45 template <typename TreeNode>
46 class TreeProcedure {
47 
48 public:
TreeProcedure()49   TreeProcedure() {}
50   virtual void visit(TreeNode* node, const Visit order) const = 0;
~TreeProcedure()51   virtual ~TreeProcedure() {}
52 };
53 
54 template <typename TreeNode>
55 class LeafProcedure {
56 
57 public:
LeafProcedure()58   LeafProcedure() {}
59   virtual void apply(TreeNode* leaf) const = 0;
~LeafProcedure()60   virtual ~LeafProcedure() {}
61 };
62 
63 /*! \brief Templated tree class.
64 
65   This class represents a tree of arity N, holding an instance of NodeData in
66   its nodes.
67  */
68 template <typename TreeNode>
69 class Tree {
70 public:
71   /// depth of the current node in the tree
72   int depth;
73 
74 protected:
75   /// empty for a leaf, pointer on a vector of sons otherwise.
76   std::vector<TreeNode*> children;
77 public:
78   /// Pointer to the father, NULL if this node is the root
79   TreeNode* father;
80 
81 public:
Tree(TreeNode * _father,int _depth=0)82   Tree(TreeNode* _father, int _depth = 0)
83     : depth(_depth), children(), father(_father) {}
~Tree()84   virtual ~Tree() {
85     for (int i=0 ; i<nrChild() ; i++)
86       if (children[i])
87         delete children[i];
88     children.clear();
89   }
90 
91   // https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
92   // "me()->" replaces "this->" when calling a method of TreeNode
me()93   TreeNode* me() {
94       return static_cast<TreeNode*>(this);
95   }
me() const96   const TreeNode* me() const {
97       return static_cast<const TreeNode*>(this);
98   }
99 
100   /*! \brief Insert a child in the children vector.
101 
102     If a child is already present, it is removed but not deleted.
103 
104     \param index index in the children vector
105     \param child pointeur to the child
106    */
insertChild(int index,TreeNode * child)107   void insertChild(int index, TreeNode *child) {
108     if (nrChild()<=index)
109       children.resize(index+1, (TreeNode*)NULL);
110     children[index] = child;
111     if (child) {
112       child->father = me();
113       child->depth = depth + 1;
114     }
115   }
116 
117   /*! \brief Remove a child, and delete it if necessary.
118    */
removeChild(int index)119   void removeChild(int index) {
120     assert(index>=0 && index<nrChild());
121     if (children[index])
122     delete children[index];
123     children[index] = (TreeNode*)NULL;
124   }
125 
126   /** Remove all children without freeing them */
detachChildren()127   void detachChildren() {
128       children.clear();
129   }
130 
131   /*! \brief Return the number of nodes in the tree.
132    */
nodesCount() const133   int nodesCount() const {
134     int result = 1;
135     for (int i=0 ; i<nrChild() ; i++)
136       if (children[i])
137         result += children[i]->nodesCount();
138     return result;
139   }
140 
141   /*! \brief Return the child of index, or NULL.
142    */
getChild(int index) const143   inline TreeNode *getChild(int index) const {
144     assert(index>=0 && index<nrChild());
145     return children[index];
146   }
getChild(int index)147   inline TreeNode *&getChild(int index)  {
148     assert(index>=0 && index<nrChild());
149     return children[index];
150   }
151 
getFather() const152   inline TreeNode *getFather() const {
153     return father;
154   }
155 
nrChild() const156   inline int nrChild() const {
157     return (int)children.size();
158   }
159 
160   /*! \brief Return true if the node is a leaf (= it has no children).
161    */
isLeaf() const162   inline bool isLeaf() const {
163     return children.empty();
164   }
165 
166   /*! \brief Return a list of nodes.
167 
168     Not used anywhere.
169    */
listNodes() const170   virtual std::list<const TreeNode*> listNodes() const {
171     std::list<const TreeNode*> result;
172     result.push_back(me());
173     for (int i=0 ; i<nrChild() ; i++)
174       if (children[i]) {
175         std::list<const TreeNode*> childNodes = children[i]->listNodes();
176         result.splice(result.end(), childNodes, childNodes.begin(), childNodes.end());
177       }
178     return result;
179   }
180 
walk(const TreeProcedure<TreeNode> * proc)181   void walk(const TreeProcedure<TreeNode> *proc) {
182     if (isLeaf()) {
183       proc->visit(me(), tree_leaf); // treatment on the leaves
184     } else {
185       proc->visit(me(), tree_preorder); // treatment on a non-leaf before recursion
186       bool first = true;
187       for (int i=0 ; i<nrChild() ; i++)
188         if (children[i]) {
189           if (!first)
190             proc->visit(me(), tree_inorder); // treatment on a non-leaf after 1st child (mainly usefull with 2 children)
191           first = false;
192           children[i]->walk(proc);
193         }
194       proc->visit(me(), tree_postorder); // treatment on a non-leaf after recursion
195     }
196   }
197 
apply_on_leaf(const LeafProcedure<TreeNode> & proc)198   void apply_on_leaf(const LeafProcedure<TreeNode>& proc) {
199     if (isLeaf()) {
200       proc.apply(me());
201     } else {
202       for (int i=0 ; i<nrChild() ; i++) {
203         if (children[i]) {
204           children[i]->apply_on_leaf(proc);
205         }
206       }
207     }
208   }
209 
210 };
211 
212 }  // end namespace hmat
213 
214 #endif  // _TREE_HPP
215