source: tags/ms_r16q2/ARBDB/TreeNode.h

Last change on this file was 14833, checked in by westram, 8 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.7 KB
Line 
1// ================================================================ //
2//                                                                  //
3//   File      : TreeNode.h                                         //
4//   Purpose   :                                                    //
5//                                                                  //
6//   Coded by Ralf Westram (coder@reallysoft.de) in December 2013   //
7//   Institute of Microbiology (Technical University Munich)        //
8//   http://www.arb-home.de/                                        //
9//                                                                  //
10// ================================================================ //
11
12#ifndef TREENODE_H
13#define TREENODE_H
14
15#ifndef ARBDBT_H
16#include "arbdbt.h"
17#endif
18#ifndef _GLIBCXX_ALGORITHM
19#include <algorithm>
20#endif
21
22#define rt_assert(cond) arb_assert(cond)
23
24#if defined(DEBUG) || defined(UNIT_TESTS) // UT_DIFF
25#define PROVIDE_TREE_STRUCTURE_TESTS
26#endif
27#if defined(DEVEL_RALF) && defined(PROVIDE_TREE_STRUCTURE_TESTS)
28#define AUTO_CHECK_TREE_STRUCTURE // Note: dramatically slows down most tree operations
29#endif
30
31class TreeRoot;
32class TreeNode;
33class ARB_edge;
34
35enum TreeOrder { // contains bit values!
36    ORDER_BIG_DOWN      = 1, // bit 0 set -> big branches down
37    ORDER_BIG_TO_EDGE   = 2, // bit 1 set -> big branches to edge
38    ORDER_BIG_TO_CENTER = 4, // bit 2 set -> big branches to center
39    ORDER_ALTERNATING   = 8, // bit 3 set -> alternate bit 0
40
41    // user visible orders:
42    BIG_BRANCHES_TO_TOP      = 0,
43    BIG_BRANCHES_TO_BOTTOM   = ORDER_BIG_DOWN,
44    BIG_BRANCHES_TO_EDGE     = ORDER_BIG_TO_EDGE,
45    BIG_BRANCHES_TO_CENTER   = ORDER_BIG_TO_CENTER,
46    BIG_BRANCHES_ALTERNATING = ORDER_BIG_TO_CENTER|ORDER_ALTERNATING,
47};
48
49#define DEFINE_READ_ACCESSORS(TYPE, ACCESS, MEMBER)     \
50    TYPE ACCESS() { return MEMBER; }                    \
51    const TYPE ACCESS() const { return MEMBER; }
52
53class TreeRoot : virtual Noncopyable {
54    TreeNode *rootNode;            // root node of the tree
55    bool      deleteWithNodes;
56
57protected:
58    void predelete() {
59        // should be called from dtor of derived class defining makeNode/destroyNode
60        if (rootNode) {
61            destroyNode(rootNode);
62            rt_assert(!rootNode);
63        }
64    }
65public:
66    explicit TreeRoot(bool deleteWithNodes_)
67        : rootNode(NULL),
68          deleteWithNodes(deleteWithNodes_)
69    {
70        /*! Create a TreeRoot for a TreeNode.
71         * Purpose:
72         * - act as TreeNode factory
73         * - place to store the current rootNode
74         * - place to store other tree related information by deriving from TreeRoot
75         *
76         * @param nodeMaker_ heap-copy of a RootedTreeNodeFactory, will be deleted when this is destructed
77         * @param deleteWithNodes_ true -> delete TreeRoot when the rootNode gets destroyed (TreeRoot needs to be a heap-copy in that case)
78         *
79         * Ressource handling of the tree structure is quite difficult (and error-prone).
80         * There are two common use-cases:
81         * 1. TreeRoot is owned by some other object/scope
82         *    - pass false for deleteWithNodes_
83         *    - you may or may not destroy (parts of) the TreeNode manually
84         * 2. TreeRoot is owned by the TreeNode
85         *    - pass true for deleteWithNodes_
86         *    - when the rootNode gets destroyed, the TreeRoot will be destroyed as well
87         */
88    }
89    virtual ~TreeRoot();
90    virtual void change_root(TreeNode *old, TreeNode *newroot);
91
92    void delete_by_node() {
93        if (deleteWithNodes) {
94            rt_assert(!rootNode);
95            delete this;
96        }
97    }
98
99    virtual TreeNode *makeNode() const             = 0;
100    virtual void destroyNode(TreeNode *node) const = 0;
101
102    DEFINE_READ_ACCESSORS(TreeNode*, get_root_node, rootNode);
103
104    ARB_edge find_innermost_edge();
105};
106
107struct TreeNode : virtual Noncopyable {
108    bool      is_leaf;
109    TreeNode *father, *leftson, *rightson;
110    GBT_LEN   leftlen, rightlen;
111    GBDATA   *gb_node;
112    char     *name;
113
114private:
115    char *remark_branch; // remark_branch normally contains some bootstrap value in format 'xx%'
116                         // if you store other info there, please make sure that this info does not start with digits!!
117                         // Otherwise the tree export routines will not work correctly!
118
119    GBT_LEN& length_ref() { return is_leftson() ? father->leftlen : father->rightlen; }
120    const GBT_LEN& length_ref() const { return const_cast<TreeNode*>(this)->length_ref(); }
121
122protected:
123    TreeNode*& self_ref() {
124        return is_leftson() ? father->leftson : father->rightson;
125    }
126    void unlink_from_father() {
127        if (father) {
128            self_ref() = NULL;
129            father     = NULL;
130        }
131    }
132
133    char *swap_remark(char *new_remark) {
134        char *result  = remark_branch;
135        remark_branch = new_remark;
136        return result;
137    }
138
139public:
140
141    DEFINE_READ_ACCESSORS(TreeNode*, get_father,   father);
142    DEFINE_READ_ACCESSORS(TreeNode*, get_leftson,  leftson);
143    DEFINE_READ_ACCESSORS(TreeNode*, get_rightson, rightson);
144
145    bool is_son_of(const TreeNode *Father) const {
146        return father == Father &&
147            (father->leftson == this || father->rightson == this);
148    }
149    bool is_leftson() const {
150        gb_assert(is_son_of(get_father())); // do only call with sons!
151        return father->leftson == this;
152    }
153    bool is_rightson() const {
154        gb_assert(is_son_of(get_father())); // do only call with sons!
155        return father->rightson == this;
156    }
157
158    bool is_inside(const TreeNode *subtree) const {
159        return this == subtree || (father && get_father()->is_inside(subtree));
160    }
161    bool is_anchestor_of(const TreeNode *descendant) const {
162        return !is_leaf && descendant != this && descendant->is_inside(this);
163    }
164    const TreeNode *ancestor_common_with(const TreeNode *other) const;
165    TreeNode *ancestor_common_with(TreeNode *other) { return const_cast<TreeNode*>(ancestor_common_with(const_cast<const TreeNode*>(other))); }
166
167    GBT_LEN get_branchlength() const { return length_ref(); }
168    void set_branchlength(GBT_LEN newlen) {
169        gb_assert(!is_nan_or_inf(newlen));
170        length_ref() = newlen;
171    }
172
173    GBT_LEN get_branchlength_unrooted() const {
174        //! like get_branchlength, but root-edge is treated correctly
175        if (father->is_root_node()) {
176            return father->leftlen+father->rightlen;
177        }
178        return get_branchlength();
179    }
180    void set_branchlength_unrooted(GBT_LEN newlen) {
181        //! like set_branchlength, but root-edge is treated correctly
182        if (father->is_root_node()) {
183            father->leftlen  = newlen/2;
184            father->rightlen = newlen-father->leftlen; // make sure sum equals newlen
185        }
186        else {
187            set_branchlength(newlen);
188        }
189    }
190
191    GBT_LEN sum_child_lengths() const;
192    GBT_LEN root_distance() const {
193        //! returns distance from node to root (including nodes own length)
194        return father ? get_branchlength()+father->root_distance() : 0.0;
195    }
196    GBT_LEN intree_distance_to(const TreeNode *other) const {
197        const TreeNode *ancestor = ancestor_common_with(other);
198        return root_distance() + other->root_distance() - 2*ancestor->root_distance();
199    }
200
201    enum bs100_mode { BS_UNDECIDED, BS_REMOVE, BS_INSERT };
202    bs100_mode toggle_bootstrap100(bs100_mode mode = BS_UNDECIDED); // toggle bootstrap '100%' <-> ''
203    void       remove_bootstrap();                                  // remove bootstrap values from subtree
204
205    void reset_branchlengths();                     // reset branchlengths of subtree to tree_defaults::LENGTH
206    void scale_branchlengths(double factor);
207
208    void bootstrap2branchlen();                     // copy bootstraps to branchlengths
209    void branchlen2bootstrap();                     // copy branchlengths to bootstraps
210
211    GBT_RemarkType parse_bootstrap(double& bootstrap) const {
212        /*! analyse 'remark_branch' and return GBT_RemarkType.
213         * If result is REMARK_BOOTSTRAP, 'bootstrap' contains the bootstrap value
214         */
215        if (!remark_branch) return REMARK_NONE;
216
217        const char *end = 0;
218        bootstrap       = strtod(remark_branch, (char**)&end);
219
220        bool is_bootstrap = end[0] == '%' && end[1] == 0;
221        return is_bootstrap ? REMARK_BOOTSTRAP : REMARK_OTHER;
222    }
223    const char *get_remark() const { return remark_branch; }
224    void use_as_remark(char *newRemark) { freeset(remark_branch, newRemark); }
225    void set_remark(const char *newRemark) { freedup(remark_branch, newRemark); }
226    void set_bootstrap(double bootstrap) { use_as_remark(GBS_global_string_copy("%i%%", int(bootstrap+0.5))); } // @@@ protect against "100%"?
227    void remove_remark() { use_as_remark(NULL); }
228
229private:
230
231    friend void TreeRoot::change_root(TreeNode *old, TreeNode *newroot);
232
233    TreeRoot *tree_root;
234
235    // ------------------
236    //      functions
237
238    void reorder_subtree(TreeOrder mode);
239
240protected:
241    void set_tree_root(TreeRoot *new_root);
242
243    bool at_root() const {
244        //! return true for root-node and its sons
245        return !father || !father->father;
246    }
247    virtual ~TreeNode() {
248        if (tree_root) {
249            rt_assert(tree_root->get_root_node() == this); // you may only free the root-node or unlinked nodes (i.e. such with tree_root==NULL)
250
251            TreeRoot *root = tree_root;
252            root->TreeRoot::change_root(this, NULL);
253            root->delete_by_node();
254        }
255        delete leftson;  gb_assert(!leftson); // cannot use destroy here
256        delete rightson; gb_assert(!rightson);
257
258        unlink_from_father();
259
260        free(name);
261        free(remark_branch);
262    }
263    void destroy()  {
264        rt_assert(knownNonNull(this));
265        TreeRoot *myRoot = get_tree_root();
266        rt_assert(myRoot); // if this fails, you need to use destroy(TreeRoot*), i.e. destroy(TreeNode*, TreeRoot*)
267        myRoot->destroyNode(this);
268    }
269    void destroy(TreeRoot *viaRoot) {
270        rt_assert(knownNonNull(this));
271#if defined(ASSERTION_USED)
272        TreeRoot *myRoot = get_tree_root();
273        rt_assert(!myRoot || myRoot == viaRoot);
274#endif
275        viaRoot->destroyNode(this);
276    }
277
278public:
279    TreeNode(TreeRoot *root)
280        : is_leaf(false),
281          father(NULL), leftson(NULL), rightson(NULL),
282          leftlen(0.0), rightlen(0.0),
283          gb_node(NULL),
284          name(NULL),
285          remark_branch(NULL),
286          tree_root(root)
287    {}
288    static void destroy(TreeNode *that)  { // replacement for destructor
289        if (that) that->destroy();
290    }
291    static void destroy(TreeNode *that, TreeRoot *root) {
292        if (that) that->destroy(root);
293    }
294
295    TreeNode *fixDeletedSon(); // @@@ review (design)
296
297    void unlink_from_DB();
298
299    void announce_tree_constructed() { // @@@ use this function or just call change_root instead?
300        // (has to be) called after tree has been constructed
301        gb_assert(!father); // has to be called with root-node!
302        get_tree_root()->change_root(NULL, this);
303    }
304
305    virtual unsigned get_leaf_count() const = 0;
306    virtual void compute_tree()             = 0;
307
308    void forget_origin() { set_tree_root(NULL); }
309    void forget_relatives() {
310        leftson  = NULL;
311        rightson = NULL;
312        father   = NULL;
313    }
314
315    TreeRoot *get_tree_root() const { return tree_root; }
316
317    const TreeNode *get_root_node() const {
318        if (!tree_root) return NULL; // nodes removed from tree have no root-node
319
320        const TreeNode *root = tree_root->get_root_node();
321        rt_assert(is_inside(root)); // this is not in tree - behavior of get_root_node() changed!
322        return root;
323    }
324    TreeNode *get_root_node() { return const_cast<TreeNode*>(const_cast<const TreeNode*>(this)->get_root_node()); }
325
326    bool is_root_node() const { return get_root_node() == this; }
327    virtual void set_root();
328
329    TreeNode *get_brother() {
330        rt_assert(!is_root_node()); // root node has no brother
331        rt_assert(father);          // this is a removed node (not root, but no father)
332        return is_leftson() ? get_father()->get_rightson() : get_father()->get_leftson();
333    }
334    const TreeNode *get_brother() const {
335        return const_cast<const TreeNode*>(const_cast<TreeNode*>(this)->get_brother());
336    }
337
338    bool is_named_group() const {
339        rt_assert(!is_leaf); // checking whether a leaf is a group
340        return gb_node && name;
341    }
342    const char *get_group_name() const {
343        return is_named_group() ? name : NULL;
344    }
345
346    virtual void swap_sons() {
347        rt_assert(!is_leaf); // @@@ if never fails -> remove condition below
348        if (!is_leaf) {
349            std::swap(leftson, rightson);
350            std::swap(leftlen, rightlen);
351        }
352    }
353    void rotate_subtree(); // flip whole subtree ( = recursive swap_sons())
354    void reorder_tree(TreeOrder mode);
355
356    TreeNode *findLeafNamed(const char *wantedName);
357
358    GBT_LEN reset_length_and_bootstrap() {
359        //! remove remark + zero but return branchlen
360        remove_remark();
361        GBT_LEN len = get_branchlength_unrooted();
362        set_branchlength_unrooted(0.0);
363        return len;
364    }
365
366    struct multifurc_limits {
367        double bootstrap;
368        double branchlength;
369        bool   applyAtLeafs;
370        multifurc_limits(double bootstrap_, double branchlength_, bool applyAtLeafs_)
371            : bootstrap(bootstrap_),
372              branchlength(branchlength_),
373              applyAtLeafs(applyAtLeafs_)
374        {}
375    };
376    class LengthCollector;
377
378    void multifurcate();
379    void set_branchlength_preserving(GBT_LEN new_len);
380
381    void multifurcate_whole_tree(const multifurc_limits& below);
382private:
383    void eliminate_and_collect(const multifurc_limits& below, LengthCollector& collect);
384public:
385
386#if defined(PROVIDE_TREE_STRUCTURE_TESTS)
387    Validity is_valid() const;
388#endif // PROVIDE_TREE_STRUCTURE_TESTS
389};
390
391inline void destroy(TreeNode *that) {
392    TreeNode::destroy(that);
393}
394inline void destroy(TreeNode *that, TreeRoot *root) {
395    TreeNode::destroy(that, root);
396}
397
398// ---------------------------------------------------------------------------------------
399//      macros to overwrite accessors in classes derived from TreeRoot or TreeNode:
400
401#define DEFINE_TREE_ROOT_ACCESSORS(RootType, TreeType)                                  \
402    DEFINE_DOWNCAST_ACCESSORS(TreeType, get_root_node, TreeRoot::get_root_node())
403
404#define DEFINE_TREE_RELATIVES_ACCESSORS(TreeType) \
405    DEFINE_DOWNCAST_ACCESSORS(TreeType, get_father, father); \
406    DEFINE_DOWNCAST_ACCESSORS(TreeType, get_leftson, leftson);  \
407    DEFINE_DOWNCAST_ACCESSORS(TreeType, get_rightson, rightson); \
408    DEFINE_DOWNCAST_ACCESSORS(TreeType, get_brother, TreeNode::get_brother()); \
409    DEFINE_DOWNCAST_ACCESSORS(TreeType, get_root_node, TreeNode::get_root_node());                                      \
410    TreeType *findLeafNamed(const char *wantedName) { return DOWNCAST(TreeType*, TreeNode::findLeafNamed(wantedName)); }
411
412#define DEFINE_TREE_ACCESSORS(RootType, TreeType)                                     \
413    DEFINE_DOWNCAST_ACCESSORS(RootType, get_tree_root, TreeNode::get_tree_root());    \
414    DEFINE_TREE_RELATIVES_ACCESSORS(TreeType)
415
416
417// -------------------------
418//      structure tests
419
420#if defined(PROVIDE_TREE_STRUCTURE_TESTS)
421template <typename TREE>
422inline Validity tree_is_valid(const TREE *tree, bool acceptNULL) {
423    if (tree) return tree->is_valid();
424    return Validity(acceptNULL, "NULL tree");
425}
426#endif
427
428#if defined(AUTO_CHECK_TREE_STRUCTURE)
429#define ASSERT_VALID_TREE(tree)         rt_assert(tree_is_valid(tree, false))
430#define ASSERT_VALID_TREE_OR_NULL(tree) rt_assert(tree_is_valid(tree, true))
431#else
432#define ASSERT_VALID_TREE(tree)
433#define ASSERT_VALID_TREE_OR_NULL(tree)
434#endif // AUTO_CHECK_TREE_STRUCTURE
435
436#if defined(PROVIDE_TREE_STRUCTURE_TESTS) && defined(UNIT_TESTS)
437
438#define TEST_EXPECT_VALID_TREE(tree)                     TEST_VALIDITY(tree_is_valid(tree, false))
439#define TEST_EXPECT_VALID_TREE_OR_NULL(tree)             TEST_VALIDITY(tree_is_valid(tree, true))
440#define TEST_EXPECT_VALID_TREE__BROKEN(tree,why)         TEST_VALIDITY__BROKEN(tree_is_valid(tree, false), why)
441#define TEST_EXPECT_VALID_TREE_OR_NULL__BROKEN(tree,why) TEST_VALIDITY__BROKEN(tree_is_valid(tree, true), why)
442
443#else
444
445#define TEST_EXPECT_VALID_TREE(tree)
446#define TEST_EXPECT_VALID_TREE_OR_NULL(tree)
447#define TEST_EXPECT_VALID_TREE__BROKEN(tree)
448#define TEST_EXPECT_VALID_TREE_OR_NULL__BROKEN(tree)
449
450#endif
451
452// --------------------
453//      SimpleTree
454
455struct SimpleRoot : public TreeRoot {
456    inline SimpleRoot();
457    inline TreeNode *makeNode() const OVERRIDE;
458    inline void destroyNode(TreeNode *node) const OVERRIDE;
459};
460
461class SimpleTree : public TreeNode {
462protected:
463    ~SimpleTree() OVERRIDE {}
464    friend class SimpleRoot;
465public:
466    SimpleTree(SimpleRoot *sroot) : TreeNode(sroot) {}
467
468    // TreeNode interface
469    unsigned get_leaf_count() const OVERRIDE {
470        rt_assert(0); // @@@ impl?
471        return 0;
472    }
473    void compute_tree() OVERRIDE {}
474};
475
476SimpleRoot::SimpleRoot() : TreeRoot(true) {}
477inline TreeNode *SimpleRoot::makeNode() const { return new SimpleTree(const_cast<SimpleRoot*>(this)); }
478inline void SimpleRoot::destroyNode(TreeNode *node) const { delete DOWNCAST(SimpleTree*,node); }
479
480// ----------------------
481//      ARB_edge_type
482
483enum ARB_edge_type {
484    EDGE_TO_ROOT, // edge points towards the root node
485    EDGE_TO_LEAF, // edge points away from the root node
486    ROOT_EDGE, // edge between sons of root node
487};
488
489class ARB_edge {
490    // ARB_edge is a directional edge between two non-root-nodes of the same tree
491
492    TreeNode    *from, *to;
493    ARB_edge_type  type;
494
495    ARB_edge_type detectType() const {
496        rt_assert(to != from);
497        rt_assert(!from->is_root_node());                // edges cannot be at root - use edge between sons of root!
498        rt_assert(!to->is_root_node());
499
500        if (from->father == to) return EDGE_TO_ROOT;
501        if (to->father == from) return EDGE_TO_LEAF;
502
503        rt_assert(from->get_brother() == to);       // no edge exists between 'from' and 'to'
504        rt_assert(to->get_father()->is_root_node());
505        return ROOT_EDGE;
506    }
507
508    GBT_LEN adjacent_distance() const;
509    GBT_LEN length_or_adjacent_distance() const {
510        {
511            GBT_LEN len = length();
512            if (len>0.0) return len;
513        }
514        return adjacent_distance();
515    }
516
517    void virtually_add_or_distribute_length_forward(GBT_LEN len, TreeNode::LengthCollector& collect) const;
518    void virtually_distribute_length_forward(GBT_LEN len, TreeNode::LengthCollector& collect) const;
519public:
520    void virtually_distribute_length(GBT_LEN len, TreeNode::LengthCollector& collect) const; // @@@ hm public :(
521private:
522
523#if defined(UNIT_TESTS) // UT_DIFF
524    friend void TEST_edges();
525#endif
526
527public:
528    ARB_edge(TreeNode *From, TreeNode *To)
529        : from(From)
530        , to(To)
531        , type(detectType())
532    {}
533    ARB_edge(TreeNode *From, TreeNode *To, ARB_edge_type Type)
534        : from(From)
535        , to(To)
536        , type(Type)
537    {
538        rt_assert(type == detectType());
539    }
540    ARB_edge(const ARB_edge& otherEdge)
541        : from(otherEdge.from)
542        , to(otherEdge.to)
543        , type(otherEdge.type)
544    {
545        rt_assert(type == detectType());
546    }
547
548    ARB_edge_type get_type() const { return type; }
549    TreeNode *source() const { return from; }
550    TreeNode *dest() const { return to; }
551
552    TreeNode *son() const  { return type == EDGE_TO_ROOT ? from : to; }
553    TreeNode *other() const  { return type == EDGE_TO_ROOT ? to : from; }
554
555    GBT_LEN length() const {
556        if (type == ROOT_EDGE) return from->get_branchlength() + to->get_branchlength();
557        return son()->get_branchlength();
558    }
559    void set_length(GBT_LEN len)  {
560        if (type == ROOT_EDGE) {
561            from->set_branchlength(len/2);
562            to->set_branchlength(len/2);
563        }
564        else {
565            son()->set_branchlength(len);
566        }
567    }
568    GBT_LEN eliminate() {
569        //! eliminates edge (zeroes length and bootstrap). returns eliminated length.
570        if (type == ROOT_EDGE) {
571            return source()->reset_length_and_bootstrap() + dest()->reset_length_and_bootstrap();
572        }
573        return son()->reset_length_and_bootstrap();
574    }
575
576    ARB_edge inverse() const {
577        return ARB_edge(to, from, ARB_edge_type(type == ROOT_EDGE ? ROOT_EDGE : (EDGE_TO_LEAF+EDGE_TO_ROOT)-type));
578    }
579
580    // iterator functions: endlessly iterate over all edges of tree
581    ARB_edge next() const { // descends rightson first
582        if (type == EDGE_TO_ROOT) {
583            rt_assert(from->is_son_of(to));
584            if (from->is_rightson()) return ARB_edge(to, to->get_leftson(), EDGE_TO_LEAF);
585            TreeNode *father = to->get_father();
586            if (father->is_root_node()) return ARB_edge(to, to->get_brother(), ROOT_EDGE);
587            return ARB_edge(to, father, EDGE_TO_ROOT);
588        }
589        if (at_leaf()) return inverse();
590        return ARB_edge(to, to->get_rightson(), EDGE_TO_LEAF);
591    }
592    ARB_edge otherNext() const { // descends leftson first (slow)
593        if (at_leaf()) return inverse();
594        return next().inverse().next();
595    }
596
597    bool operator == (const ARB_edge& otherEdge) const {
598        return from == otherEdge.from && to == otherEdge.to;
599    }
600
601    bool at_leaf() const {
602        //! true if edge is leaf edge and points towards the leaf
603        return dest()->is_leaf;
604    }
605
606    void set_root() { son()->set_root(); }
607
608    void multifurcate();
609};
610
611inline ARB_edge parentEdge(TreeNode *son) {
612    /*! returns edge to father (or to brother for sons of root).
613     * Cannot be called with root-node (but can be called with each end of any ARB_edge)
614     */
615    TreeNode *father = son->get_father();
616    rt_assert(father);
617
618    if (father->is_root_node()) return ARB_edge(son, son->get_brother(), ROOT_EDGE);
619    return ARB_edge(son, father, EDGE_TO_ROOT);
620}
621
622#else
623#error TreeNode.h included twice
624#endif // TREENODE_H
Note: See TracBrowser for help on using the repository browser.