root/trunk/ARBDB/adtree.cxx

Revision 8701, 47.2 KB (checked in by westram, 3 weeks ago)
  • rename
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : adtree.cxx                                        //
4//   Purpose   : tree functions                                    //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include <arbdbt.h>
12#include <arb_progress.h>
13#include "gb_local.h"
14#include <arb_strarray.h>
15#include <set>
16#include <limits.h>
17#include <arb_global_defs.h>
18
19#define GBT_PUT_DATA 1
20#define GBT_GET_SIZE 0
21
22// ----------------
23//      basics
24
25GBDATA *GBT_get_tree_data(GBDATA *gb_main) {
26    return GBT_find_or_create(gb_main, "tree_data", 7);
27}
28
29// ----------------------
30//      remove leafs
31
32
33static GBT_TREE *fixDeletedSon(GBT_TREE *tree) {
34    // fix tree after one son has been deleted
35    // (Note: this function only works correct for trees with minimum element size!)
36    GBT_TREE *delNode = tree;
37
38    if (delNode->leftson) {
39        gb_assert(!delNode->rightson);
40        tree             = delNode->leftson;
41        delNode->leftson = 0;
42    }
43    else {
44        gb_assert(!delNode->leftson);
45        gb_assert(delNode->rightson);
46
47        tree              = delNode->rightson;
48        delNode->rightson = 0;
49    }
50
51    // now tree is the new tree
52    tree->father = delNode->father;
53
54    if (delNode->remark_branch && !tree->remark_branch) { // rescue remarks if possible
55        tree->remark_branch    = delNode->remark_branch;
56        delNode->remark_branch = 0;
57    }
58    if (delNode->gb_node && !tree->gb_node) { // rescue group if possible
59        tree->gb_node    = delNode->gb_node;
60        delNode->gb_node = 0;
61    }
62
63    delNode->is_leaf = true; // don't try recursive delete
64
65    if (delNode->father) { // not root
66        GBT_delete_tree(delNode);
67    }
68    else { // root node
69        if (delNode->tree_is_one_piece_of_memory) {
70            // don't change root -> copy instead
71            memcpy(delNode, tree, sizeof(GBT_TREE));
72            tree = delNode;
73        }
74        else {
75            GBT_delete_tree(delNode);
76        }
77    }
78    return tree;
79}
80
81
82GBT_TREE *GBT_remove_leafs(GBT_TREE *tree, GBT_TREE_REMOVE_TYPE mode, GB_HASH *species_hash, int *removed, int *groups_removed) {
83    // Given 'tree' can either
84    // - be linked (in this case 'species_hash' shall be NULL)
85    // - be unlinked (in this case 'species_hash' has to be provided)
86    //
87    // If 'removed' and/or 'groups_removed' is given, it's used to count the removed leafs/groups.
88
89    if (tree->is_leaf) {
90        if (tree->name) {
91            bool    deleteSelf = false;
92            GBDATA *gb_node;
93
94            if (species_hash) {
95                gb_node = (GBDATA*)GBS_read_hash(species_hash, tree->name);
96                gb_assert(tree->gb_node == 0); // don't call linked tree with 'species_hash'!
97            }
98            else gb_node = tree->gb_node;
99
100            if (gb_node) {
101                if (mode & (GBT_REMOVE_MARKED|GBT_REMOVE_NOT_MARKED)) {
102                    long flag = GB_read_flag(gb_node);
103                    deleteSelf = (flag && (mode&GBT_REMOVE_MARKED)) || (!flag && (mode&GBT_REMOVE_NOT_MARKED));
104                }
105            }
106            else { // zombie
107                if (mode & GBT_REMOVE_DELETED) deleteSelf = true;
108            }
109
110            if (deleteSelf) {
111                GBT_delete_tree(tree);
112                if (removed) (*removed)++;
113                tree = 0;
114            }
115        }
116    }
117    else {
118        tree->leftson  = GBT_remove_leafs(tree->leftson, mode, species_hash, removed, groups_removed);
119        tree->rightson = GBT_remove_leafs(tree->rightson, mode, species_hash, removed, groups_removed);
120
121        if (tree->leftson) {
122            if (!tree->rightson) { // right son deleted
123                tree = fixDeletedSon(tree);
124            }
125            // otherwise no son deleted
126        }
127        else if (tree->rightson) { // left son deleted
128            tree = fixDeletedSon(tree);
129        }
130        else {                  // everything deleted -> delete self
131            if (tree->name && groups_removed) (*groups_removed)++;
132            tree->is_leaf = true;
133            GBT_delete_tree(tree);
134            tree          = 0;
135        }
136    }
137
138    return tree;
139}
140
141// -------------------
142//      free tree
143
144
145void GBT_delete_tree(GBT_TREE *tree)
146     /* frees a tree only in memory (not in the database)
147        to delete the tree in Database
148        just call GB_delete((GBDATA *)gb_tree);
149     */
150{
151    free(tree->name);
152    free(tree->remark_branch);
153
154    if (!tree->is_leaf) {
155        GBT_delete_tree(tree->leftson);
156        GBT_delete_tree(tree->rightson);
157    }
158    if (!tree->tree_is_one_piece_of_memory || !tree->father) {
159        free(tree);
160    }
161}
162
163// -----------------------------
164//      tree write functions
165
166GB_ERROR GBT_write_group_name(GBDATA *gb_group_name, const char *new_group_name) {
167    GB_ERROR error = 0;
168    size_t   len   = strlen(new_group_name);
169
170    if (len >= GB_GROUP_NAME_MAX) {
171        error = GBS_global_string("Group name '%s' too long (max %i characters)", new_group_name, GB_GROUP_NAME_MAX);
172    }
173    else {
174        error = GB_write_string(gb_group_name, new_group_name);
175    }
176    return error;
177}
178
179static GB_ERROR gbt_write_tree_nodes(GBDATA *gb_tree, GBT_TREE *node, long *startid) {
180    // increments '*startid' for each inner node (not for leafs)
181
182    GB_ERROR error = NULL;
183
184    if (!node->is_leaf) {
185        bool node_is_used = false;
186
187        if (node->name && node->name[0]) {
188            if (!node->gb_node) {
189                node->gb_node = GB_create_container(gb_tree, "node");
190                if (!node->gb_node) error = GB_await_error();
191            }
192            if (!error) {
193                GBDATA *gb_name     = GB_search(node->gb_node, "group_name", GB_STRING);
194                if (!gb_name) error = GB_await_error();
195                else    error       = GBT_write_group_name(gb_name, node->name);
196
197                node_is_used = true; // wrote groupname -> node is used
198            }
199        }
200
201        if (node->gb_node && !error) {
202            if (!node_is_used) {
203                GBDATA *gb_nonid = GB_child(node->gb_node);
204                while (gb_nonid && strcmp("id", GB_read_key_pntr(gb_nonid)) == 0) {
205                    gb_nonid = GB_nextChild(gb_nonid);
206                }
207                if (gb_nonid) node_is_used = true; // found child that is not "id" -> node is used
208            }
209
210            if (node_is_used) { // set id for used nodes
211                error             = GBT_write_int(node->gb_node, "id", *startid);
212                if (!error) error = GB_write_usr_private(node->gb_node, 0);
213            }
214            else {          // delete unused nodes
215                error = GB_delete(node->gb_node);
216                if (!error) node->gb_node = 0;
217            }
218        }
219
220        (*startid)++;
221        if (!error) error = gbt_write_tree_nodes(gb_tree, node->leftson, startid);
222        if (!error) error = gbt_write_tree_nodes(gb_tree, node->rightson, startid);
223    }
224    return error;
225}
226
227static char *gbt_write_tree_rek_new(const GBT_TREE *node, char *dest, long mode) {
228    char buffer[40];        // just real numbers
229    char    *c1;
230
231    if ((c1 = node->remark_branch)) {
232        int c;
233        if (mode == GBT_PUT_DATA) {
234            *(dest++) = 'R';
235            while ((c = *(c1++))) {
236                if (c == 1) continue;
237                *(dest++) = c;
238            }
239            *(dest++) = 1;
240        }
241        else {
242            dest += strlen(c1) + 2;
243        }
244    }
245
246    if (node->is_leaf) {
247        if (mode == GBT_PUT_DATA) {
248            *(dest++) = 'L';
249            if (node->name) strcpy(dest, node->name);
250            while ((c1 = (char *)strchr(dest, 1))) *c1 = 2;
251            dest += strlen(dest);
252            *(dest++) = 1;
253            return dest;
254        }
255        else {
256            if (node->name) return dest+1+strlen(node->name)+1; // N name term
257            return dest+1+1;
258        }
259    }
260    else {
261        sprintf(buffer, "%g,%g;", node->leftlen, node->rightlen);
262        if (mode == GBT_PUT_DATA) {
263            *(dest++) = 'N';
264            strcpy(dest, buffer);
265            dest += strlen(buffer);
266        }
267        else {
268            dest += strlen(buffer)+1;
269        }
270        dest = gbt_write_tree_rek_new(node->leftson,  dest, mode);
271        dest = gbt_write_tree_rek_new(node->rightson, dest, mode);
272        return dest;
273    }
274}
275
276static GB_ERROR gbt_write_tree(GBDATA *gb_main, GBDATA *gb_tree, const char *tree_name, GBT_TREE *tree, int plain_only) {
277    /*! writes a tree to the database.
278     *
279     * If tree is loaded by function GBT_read_tree(..) then 'tree_name' should be NULL
280     * else 'gb_tree' should be set to NULL
281     *
282     * To copy a tree call GB_copy((GBDATA *)dest,(GBDATA *)source);
283     * or set recursively all tree->gb_node variables to zero (that unlinks the tree),
284     *
285     * if 'plain_only' == 1 only the plain tree string is written
286     */
287
288    GB_ERROR error = 0;
289
290    gb_assert(implicated(plain_only, tree_name == 0)); 
291
292    if (tree) {
293        if (tree_name) {
294            if (gb_tree) error = GBS_global_string("can't change name of existing tree (to '%s')", tree_name);
295            else {
296                error = GBT_check_tree_name(tree_name);
297                if (!error) {
298                    GBDATA *gb_tree_data = GBT_get_tree_data(gb_main);
299                    gb_tree              = GB_search(gb_tree_data, tree_name, GB_CREATE_CONTAINER);
300
301                    if (!gb_tree) error = GB_await_error();
302                }
303            }
304        }
305        else {
306            if (!gb_tree) error = "No tree name given";
307        }
308
309        gb_assert(gb_tree || error);
310
311        if (!error) {
312            if (!plain_only) {
313                // mark all old style tree data for deletion
314                GBDATA *gb_node;
315                for (gb_node = GB_entry(gb_tree, "node"); gb_node; gb_node = GB_nextEntry(gb_node)) {
316                    GB_write_usr_private(gb_node, 1);
317                }
318            }
319
320            // build tree-string and save to DB
321            {
322                char *t_size = gbt_write_tree_rek_new(tree, 0, GBT_GET_SIZE); // calc size of tree-string
323                char *ctree  = (char *)GB_calloc(sizeof(char), (size_t)(t_size+1)); // allocate buffer for tree-string
324
325                t_size = gbt_write_tree_rek_new(tree, ctree, GBT_PUT_DATA); // write into buffer
326                *(t_size) = 0;
327
328                bool was_allowed = GB_allow_compression(gb_main, false);
329                error            = GBT_write_string(gb_tree, "tree", ctree);
330                GB_allow_compression(gb_main, was_allowed);
331                free(ctree);
332            }
333        }
334
335        if (!plain_only && !error) {
336            // save nodes to DB
337            long size         = 0;
338            error             = gbt_write_tree_nodes(gb_tree, tree, &size); // reports number of nodes in 'size'
339            if (!error) error = GBT_write_int(gb_tree, "nnodes", size);
340
341            if (!error) {
342                GBDATA *gb_node;
343                GBDATA *gb_node_next;
344
345                for (gb_node = GB_entry(gb_tree, "node"); // delete all ghost nodes
346                     gb_node && !error;
347                     gb_node = gb_node_next)
348                {
349                    GBDATA *gbd = GB_entry(gb_node, "id");
350                    gb_node_next = GB_nextEntry(gb_node);
351                    if (!gbd || GB_read_usr_private(gb_node)) error = GB_delete(gb_node);
352                }
353            }
354        }
355
356        if (!error) GBT_order_tree(gb_tree);
357    }
358
359    return error;
360}
361
362GB_ERROR GBT_write_tree(GBDATA *gb_main, GBDATA *gb_tree, const char *tree_name, GBT_TREE *tree) {
363    return gbt_write_tree(gb_main, gb_tree, tree_name, tree, 0);
364}
365GB_ERROR GBT_write_tree_rem(GBDATA *gb_main, const char *tree_name, const char *remark) {
366    return GBT_write_string(GBT_find_tree(gb_main, tree_name), "remark", remark);
367}
368
369// ----------------------------
370//      tree read functions
371
372static GBT_TREE *gbt_read_tree_rek(char **data, long *startid, GBDATA **gb_tree_nodes, long structure_size, int size_of_tree, GB_ERROR *error) {
373    GBT_TREE    *node;
374    GBDATA      *gb_group_name;
375    char         c;
376    char        *p1;
377    static char *membase;
378
379    gb_assert(error);
380    if (*error) return NULL;
381
382    if (structure_size>0) {
383        node = (GBT_TREE *)GB_calloc(1, (size_t)structure_size);
384    }
385    else {
386        if (!startid[0]) {
387            membase = (char *)GB_calloc(size_of_tree+1, (size_t)(-2*structure_size)); // because of inner nodes
388        }
389        node = (GBT_TREE *)membase;
390        node->tree_is_one_piece_of_memory = 1;
391        membase -= structure_size;
392    }
393
394    c = *((*data)++);
395
396    if (c=='R') {
397        p1 = strchr(*data, 1);
398        *(p1++) = 0;
399        node->remark_branch = strdup(*data);
400        c = *(p1++);
401        *data = p1;
402    }
403
404
405    if (c=='N') {
406        p1 = (char *)strchr(*data, ',');
407        *(p1++) = 0;
408        node->leftlen = GB_atof(*data);
409        *data = p1;
410        p1 = (char *)strchr(*data, ';');
411        *(p1++) = 0;
412        node->rightlen = GB_atof(*data);
413        *data = p1;
414        if ((*startid < size_of_tree) && (node->gb_node = gb_tree_nodes[*startid])) {
415            gb_group_name = GB_entry(node->gb_node, "group_name");
416            if (gb_group_name) {
417                node->name = GB_read_string(gb_group_name);
418            }
419        }
420        (*startid)++;
421        node->leftson = gbt_read_tree_rek(data, startid, gb_tree_nodes, structure_size, size_of_tree, error);
422        if (!node->leftson) {
423            if (!node->tree_is_one_piece_of_memory) free(node);
424            return NULL;
425        }
426        node->rightson = gbt_read_tree_rek(data, startid, gb_tree_nodes, structure_size, size_of_tree, error);
427        if (!node->rightson) {
428            if (!node->tree_is_one_piece_of_memory) free(node);
429            return NULL;
430        }
431        node->leftson->father = node;
432        node->rightson->father = node;
433    }
434    else if (c=='L') {
435        node->is_leaf = true;
436        p1            = (char *)strchr(*data, 1);
437
438        gb_assert(p1);
439        gb_assert(p1[0] == 1);
440
441        *p1        = 0;
442        node->name = strdup(*data);
443        *data      = p1+1;
444    }
445    else {
446        if (!c) {
447            *error = "Unexpected end of tree definition.";
448        }
449        else {
450            *error = GBS_global_string("Can't interpret tree definition (expected 'N' or 'L' - not '%c')", c);
451        }
452        return NULL;
453    }
454    return node;
455}
456
457
458static GBT_TREE *read_tree_and_size_internal(GBDATA *gb_tree, GBDATA *gb_ctree, int structure_size, int node_count, GB_ERROR *error) {
459    GBDATA   **gb_tree_nodes;
460    GBT_TREE  *node = 0;
461
462    gb_tree_nodes = (GBDATA **)GB_calloc(sizeof(GBDATA *), (size_t)node_count);
463    if (gb_tree) {
464        GBDATA *gb_node;
465
466        for (gb_node = GB_entry(gb_tree, "node"); gb_node && !*error; gb_node = GB_nextEntry(gb_node)) {
467            long    i;
468            GBDATA *gbd = GB_entry(gb_node, "id");
469            if (!gbd) continue;
470
471            i = GB_read_int(gbd);
472            if (i<0 || i >= node_count) {
473                *error = "An inner node of the tree is corrupt";
474            }
475            else {
476                gb_tree_nodes[i] = gb_node;
477            }
478        }
479    }
480    if (!*error) {
481        char *cptr[1];
482        long  startid[1];
483        char *fbuf;
484
485        startid[0] = 0;
486        fbuf       = cptr[0] = GB_read_string(gb_ctree);
487        node       = gbt_read_tree_rek(cptr, startid, gb_tree_nodes, structure_size, (int)node_count, error);
488        free (fbuf);
489    }
490
491    free(gb_tree_nodes);
492
493    return node;
494}
495
496GBT_TREE *GBT_read_tree_and_size(GBDATA *gb_main, const char *tree_name, long structure_size, int *tree_size) {
497    /*! Loads a tree from DB into any user defined structure.
498     *
499     * Make sure that the first members of your structure look exactly like GBT_TREE!
500     *
501     * @param structure_size sizeof(yourStructure)
502     *
503     * If structure_size < 0 then the tree is allocated as just one big piece of memory,
504     * which can be freed by free((void *)root_of_tree) + deleting names or
505     * by GBT_delete_tree().
506     *
507     * @param tree_name is the name of the tree in the db
508     *
509     * @param tree_size if != NULL -> gets set to "size of tree" (aka number of leafs minus 1)
510     *
511     * @return
512     * - NULL if any error occurs (which is exported then)
513     * - root of loaded tree
514     */
515
516    GB_ERROR error = 0;
517
518    if (!tree_name) {
519        error = "no treename given";
520    }
521    else {
522        error = GBT_check_tree_name(tree_name);
523        if (!error) {
524            GBDATA *gb_tree = GBT_find_tree(gb_main, tree_name);
525
526            if (!gb_tree) {
527                error = "tree not found";
528            }
529            else {
530                GBDATA *gb_nnodes = GB_entry(gb_tree, "nnodes");
531                if (!gb_nnodes) {
532                    error = "tree is empty";
533                }
534                else {
535                    long size = GB_read_int(gb_nnodes);
536                    if (!size) {
537                        error = "has no nodes";
538                    }
539                    else {
540                        GBDATA *gb_ctree = GB_search(gb_tree, "tree", GB_FIND);
541                        if (!gb_ctree) {
542                            error = "old unsupported tree format";
543                        }
544                        else { // "new" style tree
545                            GBT_TREE *tree = read_tree_and_size_internal(gb_tree, gb_ctree, structure_size, size, &error);
546                            if (!error) {
547                                gb_assert(tree);
548                                if (tree_size) *tree_size = size; // return size of tree (=leafs-1)
549                                return tree;
550                            }
551
552                            gb_assert(!tree);
553                        }
554                    }
555                }
556            }
557        }
558    }
559
560    gb_assert(error);
561    GB_export_errorf("Failed to read tree '%s' (Reason: %s)", tree_name, error);
562    return NULL;
563}
564
565GBT_TREE *GBT_read_tree(GBDATA *gb_main, const char *tree_name, long structure_size) {
566    //! @see GBT_read_tree_and_size()
567    return GBT_read_tree_and_size(gb_main, tree_name, structure_size, 0);
568}
569
570size_t GBT_count_leafs(const GBT_TREE *tree) {
571    if (tree->is_leaf) {
572        return 1;
573    }
574    return GBT_count_leafs(tree->leftson) + GBT_count_leafs(tree->rightson);
575}
576
577static GB_ERROR gbt_invalid_because(const GBT_TREE *tree, const char *reason) {
578    return GBS_global_string("((GBT_TREE*)0x%p) %s", tree, reason);
579}
580
581inline bool has_son(const GBT_TREE *father, const GBT_TREE *son) {
582    return !father->is_leaf && (father->leftson == son || father->rightson == son);
583}
584
585static GB_ERROR gbt_is_invalid(bool is_root, const GBT_TREE *tree) {
586    if (tree->father) {
587        if (!has_son(tree->father, tree)) return gbt_invalid_because(tree, "is not son of its father");
588    }
589    else {
590        if (!is_root) return gbt_invalid_because(tree, "has no father (but isn't root)");
591    }
592
593    GB_ERROR error = NULL;
594    if (tree->is_leaf) {
595        if (tree->leftson) return gbt_invalid_because(tree, "is leaf, but has leftson");
596        if (tree->rightson) return gbt_invalid_because(tree, "is leaf, but has rightson");
597    }
598    else {
599        if (!tree->leftson) return gbt_invalid_because(tree, "is inner node, but has no leftson");
600        if (!tree->rightson) return gbt_invalid_because(tree, "is inner node, but has no rightson");
601
602        error             = gbt_is_invalid(false, tree->leftson);
603        if (!error) error = gbt_is_invalid(false, tree->rightson);
604    }
605    return error;
606}
607
608GB_ERROR GBT_is_invalid(const GBT_TREE *tree) {
609    if (tree->father) return gbt_invalid_because(tree, "is expected to be the root-node, but has father");
610    if (tree->is_leaf) return gbt_invalid_because(tree, "is expected to be the root-node, but is a leaf (tree too small)");
611    return gbt_is_invalid(true, tree);
612}
613
614// -------------------------------------------
615//      link the tree tips to the database
616
617struct link_tree_data {
618    GB_HASH      *species_hash;
619    GB_HASH      *seen_species;                     // used to count duplicates
620    arb_progress *progress;
621    int           zombies;                          // counts zombies
622    int           duplicates;                       // counts duplicates
623};
624
625static GB_ERROR gbt_link_tree_to_hash_rek(GBT_TREE *tree, link_tree_data *ltd) {
626    GB_ERROR error = 0;
627    if (tree->is_leaf) {
628        tree->gb_node = 0;
629        if (tree->name) {
630            GBDATA *gbd = (GBDATA*)GBS_read_hash(ltd->species_hash, tree->name);
631            if (gbd) tree->gb_node = gbd;
632            else ltd->zombies++;
633
634            if (ltd->seen_species) {
635                if (GBS_read_hash(ltd->seen_species, tree->name)) ltd->duplicates++;
636                else GBS_write_hash(ltd->seen_species, tree->name, 1);
637            }
638        }
639
640        if (ltd->progress) ++(*ltd->progress);
641    }
642    else {
643        error = gbt_link_tree_to_hash_rek(tree->leftson, ltd);
644        if (!error) error = gbt_link_tree_to_hash_rek(tree->rightson, ltd);
645    }
646    return error;
647}
648
649static GB_ERROR GBT_link_tree_using_species_hash(GBT_TREE *tree, bool show_status, GB_HASH *species_hash, int *zombies, int *duplicates) {
650    GB_ERROR       error;
651    link_tree_data ltd;
652    long           leafs = 0;
653
654    if (duplicates || show_status) {
655        leafs = GBT_count_leafs(tree);
656    }
657
658    ltd.species_hash = species_hash;
659    ltd.seen_species = leafs ? GBS_create_hash(leafs, GB_IGNORE_CASE) : 0;
660    ltd.zombies      = 0;
661    ltd.duplicates   = 0;
662
663    if (show_status) {
664        ltd.progress = new arb_progress("Relinking tree to database", leafs);
665    }
666    else {
667        ltd.progress = NULL;
668    }
669
670    error = gbt_link_tree_to_hash_rek(tree, &ltd);
671    if (ltd.seen_species) GBS_free_hash(ltd.seen_species);
672
673    if (zombies) *zombies       = ltd.zombies;
674    if (duplicates) *duplicates = ltd.duplicates;
675
676    delete ltd.progress;
677
678    return error;
679}
680
681GB_ERROR GBT_link_tree(GBT_TREE *tree, GBDATA *gb_main, bool show_status, int *zombies, int *duplicates) {
682    /*! Link a given tree to the database. That means that for all tips the member
683     * 'gb_node' is set to the database container holding the species data.
684     *
685     * @param zombies if != NULL -> set to number of zombies (aka non-existing species) in tree
686     * @param duplicates if != NULL -> set to number of duplicated species in tree
687     *
688     * @return error on failure
689     *
690     * @see GBT_unlink_tree()
691     */
692
693    GB_HASH  *species_hash = GBT_create_species_hash(gb_main);
694    GB_ERROR  error        = GBT_link_tree_using_species_hash(tree, show_status, species_hash, zombies, duplicates);
695
696    GBS_free_hash(species_hash);
697
698    return error;
699}
700
701void GBT_unlink_tree(GBT_TREE *tree) {
702    /*! Unlink tree from the database.
703     * @see GBT_link_tree()
704     */
705    tree->gb_node = 0;
706    if (!tree->is_leaf) {
707        GBT_unlink_tree(tree->leftson);
708        GBT_unlink_tree(tree->rightson);
709    }
710}
711
712// ---------------------
713//      trees order
714
715inline int get_tree_idx(GBDATA *gb_tree) {
716    GBDATA *gb_order = GB_entry(gb_tree, "order");
717    int     idx      = 0;
718    if (gb_order) {
719        idx = GB_read_int(gb_order);
720        gb_assert(idx>0); // invalid index
721    }
722    return idx;
723}
724
725inline int get_max_tree_idx(GBDATA *gb_treedata) {
726    int max_idx = 0;
727    for (GBDATA *gb_tree = GB_child(gb_treedata); gb_tree; gb_tree = GB_nextChild(gb_tree)) {
728        int idx = get_tree_idx(gb_tree);
729        if (idx>max_idx) max_idx = idx;
730    }
731    return max_idx;
732}
733
734inline GBDATA *get_tree_with_idx(GBDATA *gb_treedata, int at_idx) {
735    GBDATA *gb_found = NULL;
736    for (GBDATA *gb_tree = GB_child(gb_treedata); gb_tree && !gb_found; gb_tree = GB_nextChild(gb_tree)) {
737        int idx = get_tree_idx(gb_tree);
738        if (idx == at_idx) {
739            gb_found = gb_tree;
740        }
741    }
742    return gb_found;
743}
744
745inline GBDATA *get_tree_infrontof_idx(GBDATA *gb_treedata, int infrontof_idx) {
746    GBDATA *gb_infrontof = NULL;
747    if (infrontof_idx) {
748        int best_idx = 0;
749        for (GBDATA *gb_tree = GB_child(gb_treedata); gb_tree; gb_tree = GB_nextChild(gb_tree)) {
750            int idx = get_tree_idx(gb_tree);
751            gb_assert(idx);
752            if (idx>best_idx && idx<infrontof_idx) {
753                best_idx     = idx;
754                gb_infrontof = gb_tree;
755            }
756        }
757    }
758    return gb_infrontof;
759}
760
761inline GBDATA *get_tree_behind_idx(GBDATA *gb_treedata, int behind_idx) {
762    GBDATA *gb_behind = NULL;
763    if (behind_idx) {
764        int best_idx = INT_MAX;
765        for (GBDATA *gb_tree = GB_child(gb_treedata); gb_tree; gb_tree = GB_nextChild(gb_tree)) {
766            int idx = get_tree_idx(gb_tree);
767            gb_assert(idx);
768            if (idx>behind_idx && idx<best_idx) {
769                best_idx  = idx;
770                gb_behind = gb_tree;
771            }
772        }
773    }
774    return gb_behind;
775}
776
777inline GB_ERROR set_tree_idx(GBDATA *gb_tree, int idx) {
778    GB_ERROR  error    = NULL;
779    GBDATA   *gb_order = GB_entry(gb_tree, "order");
780    if (!gb_order) {
781        gb_order = GB_create(gb_tree, "order", GB_INT);
782        if (!gb_order) error = GB_await_error();
783    }
784    if (!error) error = GB_write_int(gb_order, idx);
785    return error;
786}
787
788static GB_ERROR reserve_tree_idx(GBDATA *gb_treedata, int idx) {
789    GB_ERROR  error   = NULL;
790    GBDATA   *gb_tree = get_tree_with_idx(gb_treedata, idx);
791    if (gb_tree) {
792        error = reserve_tree_idx(gb_treedata, idx+1);
793        if (!error) error = set_tree_idx(gb_tree, idx+1);
794    }
795    return error;
796}
797
798static void ensure_trees_have_order(GBDATA *gb_treedata) {
799    GBDATA *gb_main = GB_get_father(gb_treedata);
800
801    gb_assert(GB_get_root(gb_main)       == gb_main);
802    gb_assert(GBT_get_tree_data(gb_main) == gb_treedata);
803
804    GB_ERROR  error              = NULL;
805    GBDATA   *gb_tree_order_flag = GB_search(gb_main, "/tmp/trees_have_order", GB_INT);
806
807    if (!gb_tree_order_flag) error = GB_await_error();
808    else {
809        if (GB_read_int(gb_tree_order_flag) == 0) { // not checked yet
810            int max_idx = get_max_tree_idx(gb_treedata);
811            for (GBDATA *gb_tree = GB_child(gb_treedata); gb_tree && !error; gb_tree = GB_nextChild(gb_tree)) {
812                if (!get_tree_idx(gb_tree)) {
813                    error = set_tree_idx(gb_tree, ++max_idx);
814                }
815            }
816            if (!error) error = GB_write_int(gb_tree_order_flag, 1);
817        }
818    }
819    if (error) GBK_terminatef("failed to order trees (Reason: %s)", error);
820}
821
822void GBT_order_tree(GBDATA *gb_tree) {
823    // if 'gb_tree' has no order yet, move it to the bottom (as done previously)
824    if (!get_tree_idx(gb_tree)) {
825        set_tree_idx(gb_tree, get_max_tree_idx(GB_get_father(gb_tree))+1);
826    }
827}
828
829// ----------------------
830//      search trees
831
832GBDATA *GBT_find_tree(GBDATA *gb_main, const char *tree_name) {
833    /*! @return
834     * - DB tree container associated with tree_name
835     * - NULL if no such tree exists
836     */
837    return GB_entry(GBT_get_tree_data(gb_main), tree_name);
838}
839
840inline bool is_tree(GBDATA *gb_tree) {
841    if (!gb_tree) return false;
842    GBDATA *gb_tree_data = GB_get_father(gb_tree);
843    return gb_tree_data && GB_has_key(gb_tree_data, "tree_data");
844}
845
846inline GBDATA *get_first_tree(GBDATA *gb_main) {
847    return GB_child(GBT_get_tree_data(gb_main));
848}
849
850inline GBDATA *get_next_tree(GBDATA *gb_tree) {
851    if (!gb_tree) return NULL;
852    gb_assert(is_tree(gb_tree));
853    return GB_nextChild(gb_tree);
854}
855
856GBDATA *GBT_find_largest_tree(GBDATA *gb_main) {
857    long    maxnodes   = 0;
858    GBDATA *gb_largest = NULL;
859
860    for (GBDATA *gb_tree = get_first_tree(gb_main); gb_tree; gb_tree = get_next_tree(gb_tree)) {
861        long *nnodes = GBT_read_int(gb_tree, "nnodes");
862        if (nnodes && *nnodes>maxnodes) {
863            gb_largest = gb_tree;
864            maxnodes   = *nnodes;
865        }
866    }
867    return gb_largest;
868}
869
870GBDATA *GBT_tree_infrontof(GBDATA *gb_tree) {
871    GBDATA *gb_treedata = GB_get_father(gb_tree);
872    ensure_trees_have_order(gb_treedata);
873    return get_tree_infrontof_idx(gb_treedata, get_tree_idx(gb_tree));
874}
875GBDATA *GBT_tree_behind(GBDATA *gb_tree) {
876    GBDATA *gb_treedata = GB_get_father(gb_tree);
877    ensure_trees_have_order(gb_treedata);
878    return get_tree_behind_idx(gb_treedata, get_tree_idx(gb_tree));
879}
880
881GBDATA *GBT_find_top_tree(GBDATA *gb_main) {
882    GBDATA *gb_treedata = GBT_get_tree_data(gb_main);
883    ensure_trees_have_order(gb_treedata);
884
885    GBDATA *gb_top = get_tree_with_idx(gb_treedata, 1);
886    if (!gb_top) gb_top = get_tree_behind_idx(gb_treedata, 1);
887    return gb_top;
888}
889GBDATA *GBT_find_bottom_tree(GBDATA *gb_main) {
890    GBDATA *gb_treedata = GBT_get_tree_data(gb_main);
891    ensure_trees_have_order(gb_treedata);
892    return get_tree_infrontof_idx(gb_treedata, INT_MAX);
893}
894
895const char *GBT_existing_tree(GBDATA *gb_main, const char *tree_name) {
896    // search for a specify existing tree (and fallback to any existing)
897    GBDATA *gb_tree       = GBT_find_tree(gb_main, tree_name);
898    if (!gb_tree) gb_tree = get_first_tree(gb_main);
899    return GBT_get_tree_name(gb_tree);
900}
901
902GBDATA *GBT_find_next_tree(GBDATA *gb_tree) {
903    GBDATA *gb_other = NULL;
904    if (gb_tree) {
905        gb_other = GBT_tree_behind(gb_tree);
906        if (!gb_other) {
907            gb_other = GBT_find_top_tree(GB_get_root(gb_tree));
908            if (gb_other == gb_tree) gb_other = NULL;
909        }
910    }
911    gb_assert(gb_other != gb_tree);
912    return gb_other;
913}
914
915// --------------------
916//      tree names
917
918const char *GBT_get_tree_name(GBDATA *gb_tree) {
919    if (!gb_tree) return NULL;
920    gb_assert(is_tree(gb_tree));
921    return GB_read_key_pntr(gb_tree);
922}
923
924GB_ERROR GBT_check_tree_name(const char *tree_name) {
925    GB_ERROR error = GB_check_key(tree_name);
926    if (!error) {
927        if (strncmp(tree_name, "tree_", 5) != 0) {
928            error = "has to start with 'tree_'";
929        }
930    }
931    if (error) {
932        error = GBS_global_string("not a valid treename '%s' (Reason: %s)", tree_name, error);
933    }
934    return error;
935}
936
937const char *GBT_name_of_largest_tree(GBDATA *gb_main) {
938    return GBT_get_tree_name(GBT_find_largest_tree(gb_main));
939}
940
941const char *GBT_name_of_bottom_tree(GBDATA *gb_main) {
942    return GBT_get_tree_name(GBT_find_bottom_tree(gb_main));
943}
944
945// -------------------
946//      tree info
947
948const char *GBT_tree_info_string(GBDATA *gb_main, const char *tree_name, int maxTreeNameLen) {
949    // maxTreeNameLen shall be the max len of the longest tree name (or -1 -> do not format)
950
951    const char *result  = 0;
952    GBDATA     *gb_tree = GBT_find_tree(gb_main, tree_name);
953
954    if (!gb_tree) {
955        GB_export_errorf("tree '%s' not found", tree_name);
956    }
957    else {
958        GBDATA *gb_nnodes = GB_entry(gb_tree, "nnodes");
959        if (!gb_nnodes) {
960            GB_export_errorf("nnodes not found in tree '%s'", tree_name);
961        }
962        else {
963            const char *sizeInfo = GBS_global_string("(%li:%i)", GB_read_int(gb_nnodes)+1, GB_read_security_write(gb_tree));
964            GBDATA     *gb_rem   = GB_entry(gb_tree, "remark");
965            int         len;
966
967            if (maxTreeNameLen == -1) {
968                result = GBS_global_string("%s %11s", tree_name, sizeInfo);
969                len    = strlen(tree_name);
970            }
971            else {
972                result = GBS_global_string("%-*s %11s", maxTreeNameLen, tree_name, sizeInfo);
973                len    = maxTreeNameLen;
974            }
975            if (gb_rem) {
976                const char *remark    = GB_read_char_pntr(gb_rem);
977                const int   remarkLen = 800;
978                char       *res2      = GB_give_other_buffer(remark, len+1+11+2+remarkLen+1);
979
980                strcpy(res2, result);
981                strcat(res2, "  ");
982                strncat(res2, remark, remarkLen);
983
984                result = res2;
985            }
986        }
987    }
988    return result;
989}
990
991long GBT_size_of_tree(GBDATA *gb_main, const char *tree_name) {
992    // return the number of inner nodes in binary tree (or -1 if unknown)
993    // Note:
994    //        leafs                        = size + 1
995    //        inner nodes in unrooted tree = size - 1
996
997    long    nnodes  = -1;
998    GBDATA *gb_tree = GBT_find_tree(gb_main, tree_name);
999    if (gb_tree) {
1000        GBDATA *gb_nnodes = GB_entry(gb_tree, "nnodes");
1001        if (gb_nnodes) {
1002            nnodes = GB_read_int(gb_nnodes);
1003        }
1004    }
1005    return nnodes;
1006}
1007
1008
1009struct indexed_name {
1010    int         idx;
1011    const char *name;
1012
1013    bool operator<(const indexed_name& other) const { return idx < other.idx; }
1014};
1015
1016void GBT_get_tree_names(ConstStrArray& names, GBDATA *gb_main, bool sorted) {
1017    // stores tree names in 'names'
1018
1019    GBDATA *gb_treedata = GBT_get_tree_data(gb_main);
1020    ensure_trees_have_order(gb_treedata);
1021
1022    long tree_count = GB_number_of_subentries(gb_treedata);
1023
1024    names.reserve(tree_count);
1025    typedef std::set<indexed_name> ordered_trees;
1026    ordered_trees trees;
1027
1028    {
1029        int t     = 0;
1030        int count = 0;
1031        for (GBDATA *gb_tree = GB_child(gb_treedata); gb_tree; gb_tree = GB_nextChild(gb_tree), ++t) {
1032            indexed_name iname;
1033            iname.name = GB_read_key_pntr(gb_tree);
1034            iname.idx  = sorted ? get_tree_idx(gb_tree) : ++count;
1035
1036            trees.insert(iname);
1037        }
1038    }
1039
1040    if (tree_count != (long)trees.size()) { // there are duplicated "order" entries
1041        gb_assert(sorted); // should not happen in unsorted mode
1042
1043        typedef std::set<int> ints;
1044
1045        ints    used_indices;
1046        GBDATA *gb_first_tree = GB_child(gb_treedata);
1047        GBDATA *gb_tree       = gb_first_tree;
1048
1049        while (gb_tree) {
1050            int idx = get_tree_idx(gb_tree);
1051            if (used_indices.find(idx) != used_indices.end()) { // duplicate order
1052                GB_ERROR error    = reserve_tree_idx(gb_treedata, idx+1);
1053                if (!error) error = set_tree_idx(gb_tree, idx+1);
1054                if (error) GBK_terminatef("failed to fix tree-order (Reason: %s)", error);
1055
1056                // now restart
1057                used_indices.clear();
1058                gb_tree = gb_first_tree;
1059            }
1060            else {
1061                used_indices.insert(idx);
1062                gb_tree = GB_nextChild(gb_tree);
1063            }
1064        }
1065        GBT_get_tree_names(names, gb_main, sorted);
1066        return;
1067    }
1068
1069    for (ordered_trees::const_iterator t = trees.begin(); t != trees.end(); ++t) {
1070        names.put(t->name);
1071    }
1072}
1073
1074NOT4PERL GB_ERROR GBT_move_tree(GBDATA *gb_moved_tree, GBT_ORDER_MODE mode, GBDATA *gb_target_tree) {
1075    // moves 'gb_moved_tree' next to 'gb_target_tree' (only changes tree-order)
1076    gb_assert(gb_moved_tree && gb_target_tree);
1077
1078    GBDATA *gb_treedata = GB_get_father(gb_moved_tree);
1079    ensure_trees_have_order(gb_treedata);
1080
1081    int target_idx = get_tree_idx(gb_target_tree);
1082    gb_assert(target_idx);
1083
1084    if (mode == GBT_BEHIND) target_idx++;
1085
1086    GB_ERROR error    = reserve_tree_idx(gb_treedata, target_idx);
1087    if (!error) error = set_tree_idx(gb_moved_tree, target_idx);
1088
1089    return error;
1090}
1091
1092static GBDATA *get_source_and_check_target_tree(GBDATA *gb_main, const char *source_tree, const char *dest_tree, GB_ERROR& error) {
1093    GBDATA *gb_source_tree = NULL;
1094
1095    error             = GBT_check_tree_name(source_tree);
1096    if (!error) error = GBT_check_tree_name(dest_tree);
1097
1098    if (error && strcmp(source_tree, NO_TREE_SELECTED) == 0) {
1099        error = "No tree selected";
1100    }
1101
1102    if (!error && strcmp(source_tree, dest_tree) == 0) error = "source- and dest-tree are the same";
1103
1104    if (!error) {
1105        gb_source_tree = GBT_find_tree(gb_main, source_tree);
1106        if (!gb_source_tree) error = GBS_global_string("tree '%s' not found", source_tree);
1107        else {
1108            GBDATA *gb_dest_tree = GBT_find_tree(gb_main, dest_tree);
1109            if (gb_dest_tree) {
1110                error = GBS_global_string("tree '%s' already exists", dest_tree);
1111                gb_source_tree = NULL;
1112            }
1113        }
1114    }
1115
1116    gb_assert(contradicted(error, gb_source_tree));
1117    return gb_source_tree;
1118}
1119
1120static GBDATA *copy_tree_container(GBDATA *gb_source_tree, const char *newName, GB_ERROR& error) {
1121    GBDATA *gb_treedata  = GB_get_father(gb_source_tree);
1122    GBDATA *gb_dest_tree = GB_create_container(gb_treedata, newName);
1123
1124    if (!gb_dest_tree) error = GB_await_error();
1125    else error               = GB_copy(gb_dest_tree, gb_source_tree);
1126
1127    gb_assert(contradicted(error, gb_dest_tree));
1128    return gb_dest_tree;
1129}
1130
1131GB_ERROR GBT_copy_tree(GBDATA *gb_main, const char *source_name, const char *dest_name) {
1132    GB_ERROR  error;
1133    GBDATA   *gb_source_tree = get_source_and_check_target_tree(gb_main, source_name, dest_name, error);
1134
1135    if (gb_source_tree) {
1136        GBDATA *gb_dest_tree = copy_tree_container(gb_source_tree, dest_name, error);
1137        if (gb_dest_tree) {
1138            int source_idx = get_tree_idx(gb_source_tree);
1139            int dest_idx   = source_idx+1;
1140
1141            error             = reserve_tree_idx(GB_get_father(gb_dest_tree), dest_idx);
1142            if (!error) error = set_tree_idx(gb_dest_tree, dest_idx);
1143        }
1144    }
1145
1146    return error;
1147}
1148
1149GB_ERROR GBT_rename_tree(GBDATA *gb_main, const char *source_name, const char *dest_name) {
1150    GB_ERROR  error;
1151    GBDATA   *gb_source_tree = get_source_and_check_target_tree(gb_main, source_name, dest_name, error);
1152
1153    if (gb_source_tree) {
1154        GBDATA *gb_dest_tree = copy_tree_container(gb_source_tree, dest_name, error);
1155        if (gb_dest_tree) error = GB_delete(gb_source_tree);
1156    }
1157
1158    return error;
1159}
1160
1161static GB_CSTR *fill_species_name_array(GB_CSTR *current, const GBT_TREE *tree) {
1162    if (tree->is_leaf) {
1163        current[0] = tree->name;
1164        return current+1;
1165    }
1166    current = fill_species_name_array(current, tree->leftson);
1167    current = fill_species_name_array(current, tree->rightson);
1168    return current;
1169}
1170
1171GB_CSTR *GBT_get_names_of_species_in_tree(const GBT_TREE *tree, size_t *count) {
1172    /* creates an array of all species names in a tree,
1173     * The names are not allocated (so they may change as side effect of renaming species) */
1174
1175    size_t   size   = GBT_count_leafs(tree);
1176    GB_CSTR *result = (GB_CSTR *)GB_calloc(sizeof(char *), size + 1);
1177   
1178    IF_ASSERTION_USED(GB_CSTR *check =) fill_species_name_array(result, tree);
1179    gb_assert(check - size == result);
1180
1181    if (count) *count = size;
1182
1183    return result;
1184}
1185
1186// --------------------------------------------------------------------------------
1187
1188#ifdef UNIT_TESTS
1189#include <test_unit.h>
1190
1191static const char *getTreeOrder(GBDATA *gb_main) {
1192    ConstStrArray names;
1193    GBT_get_tree_names(names, gb_main, true);
1194
1195    char *joined         = GBT_join_names(names, '|');
1196    char *size_and_names = GBS_global_string_copy("%zu:%s", names.size(), joined);
1197    free(joined);
1198
1199    RETURN_LOCAL_ALLOC(size_and_names);
1200}
1201
1202void TEST_tree_names() {
1203    TEST_ASSERT_ERROR_CONTAINS(GBT_check_tree_name(""),               "not a valid treename");
1204    TEST_ASSERT_ERROR_CONTAINS(GBT_check_tree_name("not_a_treename"), "not a valid treename");
1205    TEST_ASSERT_ERROR_CONTAINS(GBT_check_tree_name("tree_bad.dot"),   "not a valid treename");
1206
1207    TEST_ASSERT_NO_ERROR(GBT_check_tree_name("tree_")); // ugly but ok
1208    TEST_ASSERT_NO_ERROR(GBT_check_tree_name("tree_ok"));
1209}
1210
1211void TEST_tree() {
1212    GB_shell  shell;
1213    GBDATA   *gb_main = GB_open("TEST_trees.arb", "r");
1214
1215    {
1216        GB_transaction ta(gb_main);
1217
1218        {
1219            TEST_ASSERT_NULL(GBT_get_tree_name(NULL));
1220           
1221            TEST_ASSERT_EQUAL(GBT_name_of_largest_tree(gb_main), "tree_test");
1222
1223            TEST_ASSERT_EQUAL(GBT_get_tree_name(GBT_find_top_tree(gb_main)), "tree_test");
1224            TEST_ASSERT_EQUAL(GBT_name_of_bottom_tree(gb_main), "tree_nj_bs");
1225
1226            long inner_nodes = GBT_size_of_tree(gb_main, "tree_nj_bs");
1227            TEST_ASSERT_EQUAL(inner_nodes, 5);
1228            TEST_ASSERT_EQUAL(GBT_tree_info_string(gb_main, "tree_nj_bs", -1), "tree_nj_bs       (6:0)  PRG=dnadist CORR=none FILTER=none PKG=ARB");
1229            TEST_ASSERT_EQUAL(GBT_tree_info_string(gb_main, "tree_nj_bs", 20), "tree_nj_bs                 (6:0)  PRG=dnadist CORR=none FILTER=none PKG=ARB");
1230
1231            {
1232                GBT_TREE *tree = GBT_read_tree(gb_main, "tree_nj_bs", sizeof(GBT_TREE));
1233
1234                TEST_ASSERT(tree);
1235
1236                size_t leaf_count = GBT_count_leafs(tree);
1237
1238                size_t   species_count;
1239                GB_CSTR *species = GBT_get_names_of_species_in_tree(tree, &species_count);
1240
1241                StrArray  species2;
1242                for (int i = 0; species[i]; ++i) species2.put(strdup(species[i]));
1243
1244                TEST_ASSERT_EQUAL(species_count, leaf_count);
1245                TEST_ASSERT_EQUAL(long(species_count), inner_nodes+1);
1246                char *joined = GBT_join_names(species2, '*');
1247                TEST_ASSERT_EQUAL(joined, "CloButyr*CloButy2*CorGluta*CorAquat*CurCitre*CytAquat");
1248                free(joined);
1249                free(species);
1250
1251                GBT_delete_tree(tree);
1252            }
1253
1254            TEST_ASSERT_EQUAL(GBT_existing_tree(gb_main, "tree_nj_bs"), "tree_nj_bs");
1255            TEST_ASSERT_EQUAL(GBT_existing_tree(gb_main, "tree_nosuch"), "tree_test");
1256        }
1257
1258        // changing tree order
1259        {
1260            TEST_ASSERT_EQUAL(getTreeOrder(gb_main), "4:tree_test|tree_tree2|tree_nj|tree_nj_bs");
1261
1262            GBDATA *gb_test  = GBT_find_tree(gb_main, "tree_test");
1263            GBDATA *gb_tree2 = GBT_find_tree(gb_main, "tree_tree2");
1264            GBDATA *gb_nj    = GBT_find_tree(gb_main, "tree_nj");
1265            GBDATA *gb_nj_bs = GBT_find_tree(gb_main, "tree_nj_bs");
1266
1267            TEST_ASSERT_NO_ERROR(GBT_move_tree(gb_test, GBT_BEHIND, gb_nj_bs)); // move to bottom
1268            TEST_ASSERT_EQUAL(getTreeOrder(gb_main), "4:tree_tree2|tree_nj|tree_nj_bs|tree_test");
1269
1270            TEST_ASSERT_EQUAL(GBT_tree_behind(gb_tree2), gb_nj);
1271            TEST_ASSERT_EQUAL(GBT_tree_behind(gb_nj), gb_nj_bs);
1272            TEST_ASSERT_EQUAL(GBT_tree_behind(gb_nj_bs), gb_test);
1273            TEST_ASSERT_NULL(GBT_tree_behind(gb_test));
1274
1275            TEST_ASSERT_NULL(GBT_tree_infrontof(gb_tree2));
1276            TEST_ASSERT_EQUAL(GBT_tree_infrontof(gb_nj), gb_tree2);
1277            TEST_ASSERT_EQUAL(GBT_tree_infrontof(gb_nj_bs), gb_nj);
1278            TEST_ASSERT_EQUAL(GBT_tree_infrontof(gb_test), gb_nj_bs);
1279
1280            TEST_ASSERT_NO_ERROR(GBT_move_tree(gb_test, GBT_INFRONTOF, gb_tree2)); // move to top
1281            TEST_ASSERT_EQUAL(getTreeOrder(gb_main), "4:tree_test|tree_tree2|tree_nj|tree_nj_bs");
1282
1283            TEST_ASSERT_NO_ERROR(GBT_move_tree(gb_test, GBT_BEHIND, gb_tree2));
1284            TEST_ASSERT_EQUAL(getTreeOrder(gb_main), "4:tree_tree2|tree_test|tree_nj|tree_nj_bs");
1285
1286            TEST_ASSERT_NO_ERROR(GBT_move_tree(gb_nj_bs, GBT_INFRONTOF, gb_nj));
1287            TEST_ASSERT_EQUAL(getTreeOrder(gb_main), "4:tree_tree2|tree_test|tree_nj_bs|tree_nj");
1288
1289            TEST_ASSERT_NO_ERROR(GBT_move_tree(gb_nj_bs, GBT_INFRONTOF, gb_nj_bs)); // noop
1290            TEST_ASSERT_EQUAL(getTreeOrder(gb_main), "4:tree_tree2|tree_test|tree_nj_bs|tree_nj");
1291
1292            TEST_ASSERT_EQUAL(GBT_get_tree_name(GBT_find_top_tree(gb_main)), "tree_tree2");
1293
1294            TEST_ASSERT_EQUAL(GBT_get_tree_name(GBT_find_next_tree(gb_nj_bs)), "tree_nj");
1295            TEST_ASSERT_EQUAL(GBT_get_tree_name(GBT_find_next_tree(gb_nj)), "tree_tree2"); // last -> first
1296        }
1297
1298        // check tree order is maintained by copy, rename and delete
1299
1300        {
1301            // copy
1302            TEST_ASSERT_ERROR_CONTAINS(GBT_copy_tree(gb_main, "tree_nosuch", "tree_whatever"), "tree 'tree_nosuch' not found");
1303            TEST_ASSERT_ERROR_CONTAINS(GBT_copy_tree(gb_main, "tree_test",   "tree_test"),     "source- and dest-tree are the same");
1304            TEST_ASSERT_ERROR_CONTAINS(GBT_copy_tree(gb_main, "tree_tree2",  "tree_test"),     "tree 'tree_test' already exists");
1305
1306            TEST_ASSERT_NO_ERROR(GBT_copy_tree(gb_main, "tree_test", "tree_test_copy"));
1307            TEST_ASSERT(GBT_find_tree(gb_main, "tree_test_copy"));
1308            TEST_ASSERT_EQUAL(getTreeOrder(gb_main), "5:tree_tree2|tree_test|tree_test_copy|tree_nj_bs|tree_nj");
1309
1310            // rename
1311            TEST_ASSERT_NO_ERROR(GBT_rename_tree(gb_main, "tree_nj", "tree_renamed_nj"));
1312            TEST_ASSERT(GBT_find_tree(gb_main, "tree_renamed_nj"));
1313            TEST_ASSERT_EQUAL(getTreeOrder(gb_main), "5:tree_tree2|tree_test|tree_test_copy|tree_nj_bs|tree_renamed_nj");
1314
1315            TEST_ASSERT_NO_ERROR(GBT_rename_tree(gb_main, "tree_tree2", "tree_renamed_tree2"));
1316            TEST_ASSERT_EQUAL(getTreeOrder(gb_main), "5:tree_renamed_tree2|tree_test|tree_test_copy|tree_nj_bs|tree_renamed_nj");
1317
1318            TEST_ASSERT_NO_ERROR(GBT_rename_tree(gb_main, "tree_test_copy", "tree_renamed_test_copy"));
1319            TEST_ASSERT_EQUAL(getTreeOrder(gb_main), "5:tree_renamed_tree2|tree_test|tree_renamed_test_copy|tree_nj_bs|tree_renamed_nj");
1320
1321            // delete
1322
1323            GBDATA *gb_nj_bs             = GBT_find_tree(gb_main, "tree_nj_bs");
1324            GBDATA *gb_renamed_nj        = GBT_find_tree(gb_main, "tree_renamed_nj");
1325            GBDATA *gb_renamed_test_copy = GBT_find_tree(gb_main, "tree_renamed_test_copy");
1326            GBDATA *gb_renamed_tree2     = GBT_find_tree(gb_main, "tree_renamed_tree2");
1327            GBDATA *gb_test              = GBT_find_tree(gb_main, "tree_test");
1328
1329            TEST_ASSERT_NO_ERROR(GB_delete(gb_renamed_tree2));
1330            TEST_ASSERT_NO_ERROR(GB_delete(gb_renamed_test_copy));
1331            TEST_ASSERT_NO_ERROR(GB_delete(gb_renamed_nj));
1332
1333            // .. two trees left
1334
1335            TEST_ASSERT_EQUAL(getTreeOrder(gb_main), "2:tree_test|tree_nj_bs");
1336
1337            TEST_ASSERT_EQUAL(GBT_find_largest_tree(gb_main), gb_test);
1338            TEST_ASSERT_EQUAL(GBT_find_top_tree(gb_main), gb_test);
1339            TEST_ASSERT_EQUAL(GBT_find_bottom_tree(gb_main), gb_nj_bs);
1340           
1341            TEST_ASSERT_EQUAL(GBT_find_next_tree(gb_test), gb_nj_bs);
1342            TEST_ASSERT_EQUAL(GBT_find_next_tree(gb_nj_bs), gb_test);
1343
1344            TEST_ASSERT_NULL (GBT_tree_infrontof(gb_test));
1345            TEST_ASSERT_EQUAL(GBT_tree_behind   (gb_test), gb_nj_bs);
1346           
1347            TEST_ASSERT_EQUAL(GBT_tree_infrontof(gb_nj_bs), gb_test);
1348            TEST_ASSERT_NULL (GBT_tree_behind   (gb_nj_bs));
1349
1350            TEST_ASSERT_NO_ERROR(GBT_move_tree(gb_test, GBT_BEHIND, gb_nj_bs)); // move to bottom
1351            TEST_ASSERT_EQUAL(getTreeOrder(gb_main), "2:tree_nj_bs|tree_test");
1352            TEST_ASSERT_NO_ERROR(GBT_move_tree(gb_test, GBT_INFRONTOF, gb_nj_bs)); // move to top
1353            TEST_ASSERT_EQUAL(getTreeOrder(gb_main), "2:tree_test|tree_nj_bs");
1354           
1355            TEST_ASSERT_NO_ERROR(GB_delete(gb_nj_bs));
1356
1357            // .. one tree left
1358
1359            TEST_ASSERT_EQUAL(getTreeOrder(gb_main), "1:tree_test");
1360
1361            TEST_ASSERT_EQUAL(GBT_find_largest_tree(gb_main), gb_test);
1362            TEST_ASSERT_EQUAL(GBT_find_top_tree(gb_main), gb_test);
1363            TEST_ASSERT_EQUAL(GBT_find_bottom_tree(gb_main), gb_test);
1364           
1365            TEST_ASSERT_NULL(GBT_find_next_tree(gb_test)); // no other tree left
1366            TEST_ASSERT_NULL(GBT_tree_behind(gb_test));
1367            TEST_ASSERT_NULL(GBT_tree_infrontof(gb_test));
1368
1369            TEST_ASSERT_NO_ERROR(GB_delete(gb_test));
1370
1371            // .. no tree left
1372           
1373            TEST_ASSERT_EQUAL(getTreeOrder(gb_main), "0:");
1374
1375            TEST_ASSERT_NULL(GBT_find_tree(gb_main, "tree_test"));
1376            TEST_ASSERT_NULL(GBT_existing_tree(gb_main, "tree_whatever"));
1377            TEST_ASSERT_NULL(GBT_find_largest_tree(gb_main));
1378        }
1379    }
1380
1381    GB_close(gb_main);
1382}
1383
1384#endif // UNIT_TESTS
Note: See TracBrowser for help on using the browser.