source: tags/arb-6.0/NTREE/ad_trees.cxx

Last change on this file was 12267, checked in by westram, 10 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 58.8 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : ad_trees.cxx                                      //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "NT_local.h"
12#include "ad_trees.h"
13#include "NT_tree_cmp.h"
14
15#include <CT_ctree.hxx>
16
17#include <TreeAdmin.h>
18#include <TreeRead.h>
19#include <TreeWrite.h>
20#include <TreeCallbacks.hxx>
21
22#include <awt_sel_boxes.hxx>
23#include <awt_modules.hxx>
24#include <awt_TreeAwars.hxx>
25
26#include <aw_awars.hxx>
27#include <aw_edit.hxx>
28#include <aw_file.hxx>
29#include <aw_msg.hxx>
30#include <aw_root.hxx>
31#include <aw_select.hxx>
32
33#include <arb_strbuf.h>
34#include <arb_file.h>
35#include <arb_diff.h>
36
37#include <cctype>
38
39#define AWAR_TREE_SAV "ad_tree/"
40#define AWAR_TREE_TMP "tmp/ad_tree/"
41
42#define AWAR_TREE_SECURITY         AWAR_TREE_TMP "tree_security"
43#define AWAR_TREE_REM              AWAR_TREE_TMP "tree_rem"
44#define AWAR_TREE_IMPORT           AWAR_TREE_TMP "import_tree"
45#define AWAR_NODE_INFO_ONLY_MARKED AWAR_TREE_TMP "import_only_marked_node_info"
46
47#define AWAR_TREE_EXPORT_FILEBASE AWAR_TREE_TMP "export_tree"
48#define AWAR_TREE_EXPORT_FILTER   AWAR_TREE_EXPORT_FILEBASE "/filter"
49#define AWAR_TREE_EXPORT_NAME     AWAR_TREE_EXPORT_FILEBASE "/file_name"
50
51#define AWAR_TREE_EXPORT_SAV AWAR_TREE_SAV "export_tree/"
52
53#define AWAR_TREE_EXPORT_FORMAT             AWAR_TREE_EXPORT_SAV "format"
54#define AWAR_TREE_EXPORT_NDS                AWAR_TREE_EXPORT_SAV "NDS"
55#define AWAR_TREE_EXPORT_INCLUDE_BOOTSTRAPS AWAR_TREE_EXPORT_SAV "bootstraps"
56#define AWAR_TREE_EXPORT_INCLUDE_BRANCHLENS AWAR_TREE_EXPORT_SAV "branchlens"
57#define AWAR_TREE_EXPORT_INCLUDE_GROUPNAMES AWAR_TREE_EXPORT_SAV "groupnames"
58#define AWAR_TREE_EXPORT_HIDE_FOLDED_GROUPS AWAR_TREE_EXPORT_SAV "hide_folded"
59#define AWAR_TREE_EXPORT_QUOTEMODE          AWAR_TREE_EXPORT_SAV "quote_mode"
60#define AWAR_TREE_EXPORT_REPLACE            AWAR_TREE_EXPORT_SAV "replace"
61
62
63#define AWAR_TREE_CONSENSE_TMP AWAR_TREE_TMP "consense/"
64#define AWAR_TREE_CONSENSE_SAV AWAR_TREE_SAV "consense/"
65
66#define AWAR_TREE_CONSENSE_TREE     AWAR_TREE_CONSENSE_SAV "tree"
67#define AWAR_TREE_CONSENSE_SELECTED AWAR_TREE_CONSENSE_TMP "selected"
68
69static void tree_vars_callback(AW_root *aw_root) // Map tree vars to display objects
70{
71    if (GLOBAL.gb_main) {
72        GB_push_transaction(GLOBAL.gb_main);
73        char *treename = aw_root->awar(AWAR_TREE_NAME)->read_string();
74        GBDATA *ali_cont = GBT_find_tree(GLOBAL.gb_main, treename);
75        if (!ali_cont) {
76            aw_root->awar(AWAR_TREE_SECURITY)->unmap();
77            aw_root->awar(AWAR_TREE_REM)->unmap();
78        }
79        else {
80            GBDATA *tree_prot = GB_search(ali_cont, "security", GB_FIND);
81            if (!tree_prot) GBT_readOrCreate_int(ali_cont, "security", GB_read_security_write(ali_cont));
82            tree_prot         = GB_search(ali_cont, "security", GB_INT);
83
84            GBDATA *tree_rem = GB_search(ali_cont, "remark",   GB_STRING);
85            aw_root->awar(AWAR_TREE_SECURITY)->map(tree_prot);
86            aw_root->awar(AWAR_TREE_REM)     ->map(tree_rem);
87        }
88        char *suffix = aw_root->awar(AWAR_TREE_EXPORT_FILTER)->read_string();
89        char *fname  = GBS_string_eval(treename, GBS_global_string("*=*1.%s:tree_*=*1", suffix), 0);
90        aw_root->awar(AWAR_TREE_EXPORT_NAME)->write_string(fname); // create default file name
91        free(fname);
92        free(suffix);
93        GB_pop_transaction(GLOBAL.gb_main);
94        free(treename);
95    }
96}
97//  update import tree name depending on file name
98static void tree_import_callback(AW_root *aw_root) {
99    GB_transaction  ta(GLOBAL.gb_main);
100    char           *treename        = aw_root->awar(AWAR_TREE_IMPORT "/file_name")->read_string();
101    char           *treename_nopath = strrchr(treename, '/');
102
103    if (treename_nopath) {
104        ++treename_nopath;
105    }
106    else {
107        treename_nopath = treename;
108    }
109
110    char *fname = GBS_string_eval(treename_nopath, "*.tree=tree_*1:*.ntree=tree_*1:*.xml=tree_*1:.=", 0);
111    aw_root->awar(AWAR_TREE_IMPORT "/tree_name")->write_string(fname);
112
113    free(fname);
114    free(treename);
115}
116
117
118static void ad_tree_set_security(AW_root *aw_root)
119{
120    if (GLOBAL.gb_main) {
121        GB_transaction ta(GLOBAL.gb_main);
122        char *treename = aw_root->awar(AWAR_TREE_NAME)->read_string();
123        GBDATA *ali_cont = GBT_find_tree(GLOBAL.gb_main, treename);
124        if (ali_cont) {
125            long prot = aw_root->awar(AWAR_TREE_SECURITY)->read_int();
126            long old;
127            old = GB_read_security_delete(ali_cont);
128            GB_ERROR error = 0;
129            if (old != prot) {
130                error = GB_write_security_delete(ali_cont, prot);
131                if (!error)
132                    error = GB_write_security_write(ali_cont, prot);
133            }
134            if (error) aw_message(error);
135        }
136        free(treename);
137    }
138}
139
140enum ExportTreeType {
141    AD_TREE_EXPORT_FORMAT_NEWICK,
142    AD_TREE_EXPORT_FORMAT_XML,
143    AD_TREE_EXPORT_FORMAT_NEWICK_PRETTY,
144};
145
146enum ExportNodeType {
147    AD_TREE_EXPORT_NODE_SPECIES_NAME,
148    AD_TREE_EXPORT_NODE_NDS
149};
150
151static void update_filter_cb(AW_root *root) {
152    const char *filter_type = 0;
153
154    switch (ExportTreeType(root->awar(AWAR_TREE_EXPORT_FORMAT)->read_int())) {
155        case AD_TREE_EXPORT_FORMAT_XML: filter_type = "xml"; break;
156        case AD_TREE_EXPORT_FORMAT_NEWICK:
157        case AD_TREE_EXPORT_FORMAT_NEWICK_PRETTY:
158            switch (ExportNodeType(root->awar(AWAR_TREE_EXPORT_NDS)->read_int())) {
159                case AD_TREE_EXPORT_NODE_SPECIES_NAME:  filter_type = "tree"; break;
160                case AD_TREE_EXPORT_NODE_NDS:           filter_type = "ntree"; break;
161                default: nt_assert(0); break;
162            }
163            break;
164        default: nt_assert(0); break;
165    }
166
167    nt_assert(filter_type);
168    root->awar(AWAR_TREE_EXPORT_FILTER)->write_string(filter_type);
169}
170
171void create_trees_var(AW_root *aw_root, AW_default aw_def) {
172    AW_awar *awar_tree_name = aw_root->awar_string(AWAR_TREE_NAME, 0, aw_def)->set_srt(GBT_TREE_AWAR_SRT);
173
174    TreeAdmin::create_awars(aw_root, aw_def, true);
175
176    aw_root->awar_int   (AWAR_TREE_SECURITY, 0, aw_def);
177    aw_root->awar_string(AWAR_TREE_REM,      0, aw_def);
178
179    AW_create_fileselection_awars(aw_root, AWAR_TREE_EXPORT_FILEBASE, "", ".tree", "treefile");
180    aw_root->awar_int(AWAR_TREE_EXPORT_FORMAT, AD_TREE_EXPORT_FORMAT_NEWICK, aw_def)-> add_callback(update_filter_cb);
181    aw_root->awar_int(AWAR_TREE_EXPORT_NDS,  AD_TREE_EXPORT_NODE_SPECIES_NAME, aw_def)-> add_callback(update_filter_cb);
182
183    aw_root->awar_int(AWAR_TREE_EXPORT_INCLUDE_BOOTSTRAPS,  0, aw_def);
184    aw_root->awar_int(AWAR_TREE_EXPORT_INCLUDE_BRANCHLENS,  1, aw_def);
185    aw_root->awar_int(AWAR_TREE_EXPORT_HIDE_FOLDED_GROUPS,  0, aw_def);
186    aw_root->awar_int(AWAR_TREE_EXPORT_INCLUDE_GROUPNAMES,  1, aw_def);
187    aw_root->awar_int(AWAR_TREE_EXPORT_QUOTEMODE, TREE_SINGLE_QUOTES, aw_def); // old default behavior
188    aw_root->awar_int(AWAR_TREE_EXPORT_REPLACE, 0, aw_def); // old default behavior
189
190    AW_create_fileselection_awars(aw_root, AWAR_TREE_IMPORT, "", ".tree", "treefile");
191
192    aw_root->awar_string(AWAR_TREE_IMPORT "/tree_name", "tree_",    aw_def)->set_srt(GBT_TREE_AWAR_SRT);
193
194    aw_root->awar(AWAR_TREE_IMPORT "/file_name")->add_callback(tree_import_callback);
195    awar_tree_name->add_callback(tree_vars_callback);
196    awar_tree_name->map(AWAR_TREE);
197    aw_root->awar(AWAR_TREE_SECURITY)->add_callback(ad_tree_set_security);
198
199    aw_root->awar_int(AWAR_NODE_INFO_ONLY_MARKED, 0,    aw_def);
200
201    aw_root->awar_string(AWAR_TREE_CONSENSE_TREE, "tree_consensus", aw_def)->set_srt(GBT_TREE_AWAR_SRT);
202    AW_awar *ctree_awar = aw_root->awar_string(AWAR_TREE_CONSENSE_SELECTED, "", aw_def);
203    AWT_registerTreeAwarSimple(ctree_awar);
204
205    update_filter_cb(aw_root);
206    tree_vars_callback(aw_root);
207}
208
209static void tree_save_cb(AW_window *aww) {
210    AW_root  *aw_root   = aww->get_root();
211    char     *tree_name = aw_root->awar(AWAR_TREE_NAME)->read_string();
212
213    GB_ERROR error = 0;
214
215    if (!tree_name || !strlen(tree_name)) {
216        error = "Please select a tree first";
217    }
218    else {
219        char *fname   = aw_root->awar(AWAR_TREE_EXPORT_NAME)->read_string();
220        char *db_name = aw_root->awar(AWAR_DB_NAME)->read_string();
221
222        bool                use_NDS    = ExportNodeType(aw_root->awar(AWAR_TREE_EXPORT_NDS)->read_int()) == AD_TREE_EXPORT_NODE_NDS;
223        ExportTreeType      exportType = static_cast<ExportTreeType>(aw_root->awar(AWAR_TREE_EXPORT_FORMAT)->read_int());
224        TREE_node_text_gen *node_gen   = use_NDS ? new TREE_node_text_gen(make_node_text_init, make_node_text_nds) : 0;
225
226        switch (exportType) {
227            case AD_TREE_EXPORT_FORMAT_XML:
228                error = TREE_write_XML(GLOBAL.gb_main, db_name, tree_name, node_gen,
229                                       aw_root->awar(AWAR_TREE_EXPORT_HIDE_FOLDED_GROUPS)->read_int(),
230                                       fname);
231                break;
232
233            case AD_TREE_EXPORT_FORMAT_NEWICK:
234            case AD_TREE_EXPORT_FORMAT_NEWICK_PRETTY:
235                TREE_node_quoting quoteMode = TREE_node_quoting(aw_root->awar(AWAR_TREE_EXPORT_QUOTEMODE)->read_int());
236                if (aw_root->awar(AWAR_TREE_EXPORT_REPLACE)->read_int()) {
237                    quoteMode = TREE_node_quoting(quoteMode|TREE_FORCE_REPLACE);
238                }
239
240                error = TREE_write_Newick(GLOBAL.gb_main, tree_name, node_gen,
241                                          aw_root->awar(AWAR_TREE_EXPORT_INCLUDE_BRANCHLENS)->read_int(),
242                                          aw_root->awar(AWAR_TREE_EXPORT_INCLUDE_BOOTSTRAPS)->read_int(),
243                                          aw_root->awar(AWAR_TREE_EXPORT_INCLUDE_GROUPNAMES)->read_int(),
244                                          exportType == AD_TREE_EXPORT_FORMAT_NEWICK_PRETTY,
245                                          quoteMode,
246                                          fname);
247                break;
248        }
249
250        AW_refresh_fileselection(aw_root, AWAR_TREE_EXPORT_FILEBASE);
251
252        delete node_gen;
253        free(db_name);
254        free(fname);
255    }
256
257    aww->hide_or_notify(error);
258    free(tree_name);
259}
260
261static AW_window *create_tree_export_window(AW_root *root)
262{
263    AW_window_simple *aws = new AW_window_simple;
264    aws->init(root, "SAVE_TREE", "TREE SAVE");
265    aws->load_xfig("sel_box_user2.fig");
266
267    aws->callback((AW_CB0)AW_POPDOWN);
268    aws->at("close");
269    aws->create_button("CLOSE", "CLOSE", "C");
270
271    aws->at("help");
272    aws->callback(makeHelpCallback("tr_export.hlp"));
273    aws->create_button("HELP", "HELP", "H");
274
275    aws->at("user");
276    aws->create_option_menu(AWAR_TREE_EXPORT_FORMAT, true);
277    aws->insert_option("NEWICK TREE FORMAT",                   "N", AD_TREE_EXPORT_FORMAT_NEWICK);
278    aws->insert_option("NEWICK TREE FORMAT (pretty, but big)", "P", AD_TREE_EXPORT_FORMAT_NEWICK_PRETTY);
279    aws->insert_option("ARB_XML TREE FORMAT",                  "X", AD_TREE_EXPORT_FORMAT_XML);
280    aws->update_option_menu();
281
282    AW_create_standard_fileselection(aws, AWAR_TREE_EXPORT_FILEBASE);
283
284    aws->at("user2");
285    aws->auto_space(10, 10);
286    aws->label("Nodetype");
287    aws->create_toggle_field(AWAR_TREE_EXPORT_NDS, 1);
288    aws->insert_default_toggle("Species ID ('name')", "S", 0);
289    aws->insert_toggle("NDS", "N", 1);
290    aws->update_toggle_field();
291
292    aws->at_newline(); aws->label("Save branch lengths"); aws->create_toggle(AWAR_TREE_EXPORT_INCLUDE_BRANCHLENS);
293    aws->at_newline(); aws->label("Save bootstrap values"); aws->create_toggle(AWAR_TREE_EXPORT_INCLUDE_BOOTSTRAPS);
294    aws->at_newline(); aws->label("Save group names"); aws->create_toggle(AWAR_TREE_EXPORT_INCLUDE_GROUPNAMES);
295    aws->at_newline(); aws->label("Hide folded groups (XML only)"); aws->create_toggle(AWAR_TREE_EXPORT_HIDE_FOLDED_GROUPS);
296
297    aws->at_newline();
298    aws->label("Name quoting (Newick only)");
299    aws->create_option_menu(AWAR_TREE_EXPORT_QUOTEMODE, true);
300    aws->insert_option("none",            "n", TREE_DISALLOW_QUOTES);
301    aws->insert_option("single",          "s", TREE_SINGLE_QUOTES);
302    aws->insert_option("double",          "d", TREE_DOUBLE_QUOTES);
303    aws->insert_option("single (forced)", "i", TREE_SINGLE_QUOTES|TREE_FORCE_QUOTES);
304    aws->insert_option("double (forced)", "o", TREE_DOUBLE_QUOTES|TREE_FORCE_QUOTES);
305    aws->update_option_menu();
306
307    aws->at_newline(); aws->label("Replace problem chars"); aws->create_toggle(AWAR_TREE_EXPORT_REPLACE);
308
309    aws->at_newline();
310    aws->callback(tree_save_cb);
311    aws->create_button("SAVE", "SAVE", "o");
312
313    aws->callback((AW_CB0)AW_POPDOWN);
314    aws->create_button("CANCEL", "CANCEL", "C");
315
316    aws->window_fit();
317    update_filter_cb(root);
318
319    return aws;
320}
321
322static char *readXmlTree(char *fname) {
323    // create a temp file
324    char tempFile[]  = "newickXXXXXX";
325    int createTempFile = mkstemp(tempFile);
326
327    if (createTempFile) {
328        GBS_strstruct *buf = GBS_stropen(strlen(fname));
329
330        // extract path from fname in order to place a copy of dtd file required to validate xml file
331        {
332            char *tmpFname = strdup(fname);
333            for (char *tok = strtok(tmpFname, "/"); tok;) {
334                char *tmp = tok;
335                tok = strtok(0, "/");
336                if (tok) {
337                    GBS_strcat(buf, "/");
338                    GBS_strcat(buf, tmp);
339                }
340            }
341            free(tmpFname);
342        }
343
344        char *path = GBS_strclose(buf);
345
346        // linking arb_tree.dtd file to the Path from where xml file is loaded
347#if defined(WARN_TODO)
348#warning fix hack
349#endif
350        char *command = GBS_global_string_copy("ln -s %s/lib/dtd/arb_tree.dtd %s/.", GB_getenvARBHOME(), path);
351        GB_xcmd(command, false, true);
352
353        // execute xml2newick to convert xml format tree to newick format tree
354        command = GBS_global_string_copy("xml2newick %s %s", fname, tempFile);
355        GB_xcmd(command, false, true);
356
357        free(command);
358        free(path);
359
360        // return newick format tree file
361        return strdup(tempFile);
362    }
363    else {
364        printf("Failed to create Temporary File to Parse xml file!\n");
365        return 0;
366    }
367}
368
369static void tree_load_cb(AW_window *aww) {
370    GB_ERROR  error     = 0;
371    AW_root  *aw_root   = aww->get_root();
372    char     *tree_name = aw_root->awar(AWAR_TREE_IMPORT "/tree_name")->read_string();
373
374    {
375        char *pcTreeFormat = aw_root->awar(AWAR_TREE_IMPORT "/filter")->read_string();
376        char *fname        = aw_root->awar(AWAR_TREE_IMPORT "/file_name")->read_string();
377        char *warnings     = 0;
378        char *tree_comment = 0;
379
380        GBT_TREE *tree;
381        if (strcmp(pcTreeFormat, "xml") == 0) {
382            char *tempFname = readXmlTree(fname);
383            tree = TREE_load(tempFname, GBT_TREE_NodeFactory(), &tree_comment, true, &warnings);
384            GB_unlink_or_warn(tempFname, NULL);
385            free(tempFname);
386        }
387        else {
388            tree = TREE_load(fname, GBT_TREE_NodeFactory(), &tree_comment, true, &warnings);
389        }
390
391        if (!tree) error = GB_await_error();
392        else {
393            if (warnings) GBT_message(GLOBAL.gb_main, warnings);
394
395            {
396                GB_transaction ta(GLOBAL.gb_main);
397                error = GBT_write_tree_with_remark(GLOBAL.gb_main, tree_name, tree, tree_comment);
398                error = ta.close(error);
399            }
400
401            if (!error) aw_root->awar(AWAR_TREE)->write_string(tree_name); // show new tree
402
403            delete tree;
404        }
405
406        free(warnings);
407        free(tree_comment);
408        free(fname);
409        free(pcTreeFormat);
410    }
411
412    aww->hide_or_notify(error);
413    free(tree_name);
414}
415
416static AW_window *create_tree_import_window(AW_root *root)
417{
418    AW_window_simple *aws = new AW_window_simple;
419    aws->init(root, "LOAD_TREE", "TREE LOAD");
420    aws->load_xfig("sel_box_tree.fig");
421
422    aws->callback((AW_CB0)AW_POPDOWN);
423    aws->at("close");
424    aws->create_button("CLOSE", "CLOSE", "C");
425
426    aws->at("format");
427    aws->label("Tree Format");
428    aws->create_option_menu(AWAR_TREE_IMPORT "/filter", false);
429    aws->insert_default_option("Newick", "t", "tree");
430    aws->insert_option("XML", "x", "xml");
431    aws->update_option_menu();
432
433    aws->at("user");
434    aws->label("Tree name");
435    aws->create_input_field(AWAR_TREE_IMPORT "/tree_name", 15);
436
437    AW_create_standard_fileselection(aws, AWAR_TREE_IMPORT);
438
439    aws->at("save2");
440    aws->callback(tree_load_cb);
441    aws->create_button("LOAD", "LOAD", "o");
442
443    aws->callback((AW_CB0)AW_POPDOWN);
444    aws->at("cancel2");
445    aws->create_button("CANCEL", "CANCEL", "C");
446
447    aws->window_fit();
448
449    return (AW_window *)aws;
450}
451
452static void ad_move_tree_info(AW_window *aww, TreeInfoMode mode) {
453    bool nodes_with_marked_only = false;
454
455    char     *log_file = 0;
456    GB_ERROR  error    = 0;
457
458    if (mode == TREE_INFO_COPY || mode == TREE_INFO_ADD) {
459        // move or add node-info writes a log file (containing errors)
460        // compare_node_info only sets remark branches
461        char *log_name       = GB_unique_filename("arb_node", "log");
462        log_file             = GB_create_tempfile(log_name);
463        if (!log_file) error = GB_await_error();
464        free(log_name);
465
466        nodes_with_marked_only = aww->get_root()->awar(AWAR_NODE_INFO_ONLY_MARKED)->read_int();
467    }
468
469    if (!error) {
470        AW_root *awr      = aww->get_root();
471        char    *src_tree = TreeAdmin::source_tree_awar(awr)->read_string();
472        char    *dst_tree = TreeAdmin::dest_tree_awar(awr)->read_string();
473
474        error = AWT_move_info(GLOBAL.gb_main, src_tree, dst_tree, log_file, mode, nodes_with_marked_only);
475        if (log_file) {
476            AW_edit(log_file);
477            GB_remove_on_exit(log_file);
478        }
479
480        free(dst_tree);
481        free(src_tree);
482    }
483
484    if (error) aw_message(error);
485    else aww->hide();
486
487    free(log_file);
488}
489
490static void swap_source_dest_cb(AW_window *aww) {
491    AW_root *root = aww->get_root();
492
493    AW_awar *s = TreeAdmin::source_tree_awar(root);
494    AW_awar *d = TreeAdmin::dest_tree_awar(root);
495
496    char *old_src = s->read_string();
497    s->write_string(d->read_char_pntr());
498    d->write_string(old_src);
499    free(old_src);
500}
501
502static void copy_tree_awar_cb(UNFIXED, AW_awar *aw_source, AW_awar *aw_dest) {
503    const char *tree = aw_source->read_char_pntr();
504    if (tree && tree[0]) aw_dest->write_string(tree);
505}
506
507static AW_window_simple *create_select_two_trees_window(AW_root *root, const char *winId, const char *winTitle, const char *helpFile) {
508    AW_window_simple *aws = new AW_window_simple;
509    aws->init(root, winId, winTitle);
510    aws->load_xfig("ad_two_trees.fig");
511
512    aws->at("close");
513    aws->auto_space(10, 3);
514
515    aws->callback(AW_POPDOWN);
516    aws->create_button("CLOSE", "Close", "C");
517
518    aws->at("help");
519    aws->callback(makeHelpCallback(helpFile));
520    aws->create_button("HELP", "Help", "H");
521
522    aws->at("tree1");
523    awt_create_selection_list_on_trees(GLOBAL.gb_main, aws, TreeAdmin::source_tree_awar(root)->awar_name, true);
524    aws->at("tree2");
525    awt_create_selection_list_on_trees(GLOBAL.gb_main, aws, TreeAdmin::dest_tree_awar(root)->awar_name, false);
526
527    AW_awar *awar_displayed_tree = root->awar(AWAR_TREE_NAME);
528
529    aws->at("select1");
530    aws->callback(makeWindowCallback(copy_tree_awar_cb, awar_displayed_tree, TreeAdmin::source_tree_awar(root)));  aws->create_autosize_button("SELECT_DISPLAYED1", "Use");
531    aws->callback(makeWindowCallback(copy_tree_awar_cb, TreeAdmin::source_tree_awar(root), awar_displayed_tree));  aws->create_autosize_button("DISPLAY_SELECTED1", "Display");
532
533    aws->callback(swap_source_dest_cb);
534    aws->create_autosize_button("SWAP", "Swap");
535
536    aws->at("select2");
537    aws->callback(makeWindowCallback(copy_tree_awar_cb, awar_displayed_tree, TreeAdmin::dest_tree_awar(root)));  aws->create_autosize_button("SELECT_DISPLAYED2", "Use");
538    aws->callback(makeWindowCallback(copy_tree_awar_cb, TreeAdmin::dest_tree_awar(root), awar_displayed_tree));  aws->create_autosize_button("DISPLAY_SELECTED2", "Display");
539
540    aws->at("user");
541
542    return aws;
543}
544
545static AW_window_simple *create_select_other_tree_window(AW_root *root, const char *winId, const char *winTitle, const char *helpFile, const char *displayed_tree_awarname) {
546    AW_window_simple *aws = new AW_window_simple;
547    aws->init(root, winId, winTitle);
548    aws->load_xfig("ad_one_tree.fig");
549
550    aws->at("close");
551    aws->auto_space(10, 3);
552
553    aws->callback(AW_POPDOWN);
554    aws->create_button("CLOSE", "Close", "C");
555
556    aws->at("help");
557    aws->callback(makeHelpCallback(helpFile));
558    aws->create_button("HELP", "Help", "H");
559
560    AW_awar *awar_displayed_tree = root->awar(displayed_tree_awarname);
561
562    aws->at("tree");
563    awt_create_selection_list_on_trees(GLOBAL.gb_main, aws, TreeAdmin::source_tree_awar(root)->awar_name, true);
564
565    aws->at("select");
566    aws->callback(makeWindowCallback(copy_tree_awar_cb, awar_displayed_tree, TreeAdmin::source_tree_awar(root)));  aws->create_autosize_button("SELECT_DISPLAYED", "Use");
567    aws->callback(makeWindowCallback(copy_tree_awar_cb, TreeAdmin::source_tree_awar(root), awar_displayed_tree));  aws->create_autosize_button("DISPLAY_SELECTED", "Display");
568
569    aws->at("user");
570
571    return aws;
572}
573
574static AW_window *create_tree_diff_window(AW_root *root) {
575    AW_window_simple *aws = create_select_two_trees_window(root, "CMP_TOPOLOGY", "Compare tree topologies", "tree_diff.hlp");
576
577    aws->callback(makeWindowCallback(ad_move_tree_info, TREE_INFO_COMPARE));
578    aws->create_autosize_button("CMP_TOPOLOGY", "Compare topologies");
579
580    return aws;
581}
582
583static AW_window *create_tree_cmp_window(AW_root *root) {
584    AW_window_simple *aws = create_select_two_trees_window(root, "COPY_NODE_INFO_OF_TREE", "Move tree node info", "tree_cmp.hlp");
585
586    aws->button_length(11);
587
588    aws->callback(makeWindowCallback(ad_move_tree_info, TREE_INFO_COPY));
589    aws->create_button("COPY_INFO", "Copy info");
590
591    aws->label("copy/add only info containing marked species");
592    aws->create_toggle(AWAR_NODE_INFO_ONLY_MARKED);
593
594    aws->at_newline();
595
596    aws->callback(makeWindowCallback(ad_move_tree_info, TREE_INFO_ADD));
597    aws->create_button("ADD_INFO", "Add info");
598
599    return aws;
600}
601
602static void reorder_trees_cb(AW_window *aww, awt_reorder_mode dest, AW_CL) {
603    // moves the tree in the list of trees
604
605    char     *tree_name = aww->get_root()->awar(AWAR_TREE_NAME)->read_string();
606    GB_ERROR  error     = NULL;
607
608    GB_transaction ta(GLOBAL.gb_main);
609    GBDATA *gb_treedata   = GBT_get_tree_data(GLOBAL.gb_main);
610    GBDATA *gb_moved_tree = GB_entry(gb_treedata, tree_name);
611
612    if (!gb_moved_tree) {
613        error = "No tree selected";
614    }
615    else {
616        GBT_ORDER_MODE  move_mode;
617        GBDATA         *gb_target_tree = NULL;
618
619        switch (dest) {
620            case ARM_UP:
621                move_mode      = GBT_INFRONTOF;
622                gb_target_tree = GBT_tree_infrontof(gb_moved_tree);
623                if (gb_target_tree) break;
624                // fall-through (move top-tree up = move to bottom)
625            case ARM_BOTTOM:
626                move_mode      = GBT_BEHIND;
627                gb_target_tree = GBT_find_bottom_tree(GLOBAL.gb_main);
628                break;
629
630            case ARM_DOWN:
631                move_mode      = GBT_BEHIND;
632                gb_target_tree = GBT_tree_behind(gb_moved_tree);
633                if (gb_target_tree) break;
634                // fall-through (move bottom-tree down = move to top)
635            case ARM_TOP:
636                move_mode      = GBT_INFRONTOF;
637                gb_target_tree = GBT_find_top_tree(GLOBAL.gb_main);
638                break;
639        }
640
641        if (gb_target_tree && gb_target_tree != gb_moved_tree) {
642            error = GBT_move_tree(gb_moved_tree, move_mode, gb_target_tree);
643        }
644    }
645
646    if (error) aw_message(error);
647    free(tree_name);
648}
649
650void popup_tree_admin_window(AW_window *aws) {
651    popup_tree_admin_window(aws->get_root());
652}
653void popup_tree_admin_window(AW_root *aw_root) {
654    static AW_window_simple *aws = 0;
655    if (!aws) {
656        aws = new AW_window_simple;
657        aws->init(aw_root, "TREE_ADMIN", "TREE ADMIN");
658        aws->load_xfig("ad_tree.fig");
659
660        aws->callback(AW_POPDOWN);
661        aws->at("close");
662        aws->create_button("CLOSE", "CLOSE", "C");
663
664        aws->callback(makeHelpCallback("treeadm.hlp"));
665        aws->at("help");
666        aws->create_button("HELP", "HELP", "H");
667
668        aws->button_length(40);
669
670        aws->at("sel");
671        aws->create_button(0, AWAR_TREE_NAME, 0, "+");
672
673        aws->at("security");
674        aws->create_option_menu(AWAR_TREE_SECURITY, true);
675        aws->insert_option("0", "0", 0);
676        aws->insert_option("1", "1", 1);
677        aws->insert_option("2", "2", 2);
678        aws->insert_option("3", "3", 3);
679        aws->insert_option("4", "4", 4);
680        aws->insert_option("5", "5", 5);
681        aws->insert_default_option("6", "6", 6);
682        aws->update_option_menu();
683
684        aws->at("rem");
685        aws->create_text_field(AWAR_TREE_REM);
686
687
688        aws->button_length(20);
689
690        static TreeAdmin::Spec spec(GLOBAL.gb_main, AWAR_TREE_NAME);
691
692        aws->at("delete");
693        aws->callback(makeWindowCallback(TreeAdmin::delete_tree_cb, &spec));
694        aws->create_button("DELETE", "Delete", "D");
695
696        aws->at("rename");
697        aws->callback(makeCreateWindowCallback(TreeAdmin::create_rename_window, &spec));
698        aws->create_button("RENAME", "Rename", "R");
699
700        aws->at("copy");
701        aws->callback(makeCreateWindowCallback(TreeAdmin::create_copy_window, &spec));
702        aws->create_button("COPY", "Copy", "C");
703
704        aws->at("move");
705        aws->callback(create_tree_cmp_window);
706        aws->create_button("MOVE_NODE_INFO", "Move node info", "C");
707
708        aws->at("cmp");
709        aws->callback(create_tree_diff_window);
710        aws->sens_mask(AWM_EXP);
711        aws->create_button("CMP_TOPOLOGY", "Compare topology", "T");
712        aws->sens_mask(AWM_ALL);
713
714        aws->at("export");
715        aws->callback(AW_POPUP, (AW_CL)create_tree_export_window, 0);
716        aws->create_button("EXPORT", "Export", "E");
717
718        aws->at("import");
719        aws->callback(AW_POPUP, (AW_CL)create_tree_import_window, 0);
720        aws->create_button("IMPORT", "Import", "I");
721
722        aws->button_length(0);
723
724        aws->at("list");
725        awt_create_selection_list_on_trees(GLOBAL.gb_main, aws, AWAR_TREE_NAME, true);
726
727        aws->at("sort");
728        awt_create_order_buttons(aws, reorder_trees_cb, 0);
729    }
730
731    aws->activate();
732}
733
734// -----------------------
735//      consense tree
736
737
738static void create_consense_tree_cb(AW_window *aww, AW_CL cl_selected_trees) {
739    AW_root  *aw_root = aww->get_root();
740    GB_ERROR  error   = NULL;
741
742    const char *cons_tree_name = aw_root->awar(AWAR_TREE_CONSENSE_TREE)->read_char_pntr();
743    if (!cons_tree_name || !cons_tree_name[0]) {
744        error = "No name specified for the consensus tree";
745    }
746    else {
747        AW_selection *selected_trees = (AW_selection*)cl_selected_trees;
748
749        StrArray tree_names;
750        selected_trees->get_values(tree_names);
751
752        if (tree_names.size()<2) {
753            error = "Not enough trees selected (at least 2 needed)";
754        }
755        else {
756            GBDATA *gb_main = GLOBAL.gb_main;
757            GB_transaction ta(gb_main);
758
759            {
760                arb_progress progress("Building consensus tree", 2); // 2 steps: deconstruct, reconstruct
761                ConsensusTreeBuilder tree_builder;
762
763                progress.subtitle("loading input trees");
764                for (size_t t = 0; t<tree_names.size() && !error; ++t) {
765                    TreeRoot      *root = new TreeRoot(new SizeAwareNodeFactory, true); // will be deleted when tree gets deleted
766                    SizeAwareTree *tree = DOWNCAST(SizeAwareTree*, GBT_read_tree(gb_main, tree_names[t], *root));
767                    if (!tree) {
768                        error = GB_await_error();
769                    }
770                    else {
771                        tree_builder.add(tree, tree_names[t], 1.0);
772                    }
773                }
774
775                if (!error) {
776                    size_t    species_count;
777                    GBT_TREE *cons_tree = tree_builder.get(species_count, error); // triggers 2 implicit progress increments
778
779                    if (!error && progress.aborted()) {
780                        error = "user abort";
781                    }
782
783                    nt_assert(contradicted(cons_tree, error));
784                    if (cons_tree) {
785                        char *comment = tree_builder.get_remark();
786                        error         = GBT_write_tree_with_remark(gb_main, cons_tree_name, cons_tree, comment);
787                        free(comment);
788                        delete cons_tree;
789                    }
790                }
791                if (error) progress.done();
792            }
793            error = ta.close(error);
794        }
795    }
796
797    if (!error) {
798        aw_root->awar(AWAR_TREE_NAME)->write_string(cons_tree_name); // show in main window
799    }
800
801    aw_message_if(error);
802}
803
804static void use_selected_as_target_cb(AW_window *aww) {
805    AW_root *aw_root = aww->get_root();
806    aw_root->awar(AWAR_TREE_CONSENSE_TREE)->write_string(aw_root->awar(AWAR_TREE_CONSENSE_SELECTED)->read_char_pntr());
807}
808
809AW_window *NT_create_consense_window(AW_root *aw_root) {
810    static AW_window_simple *aws = 0;
811    if (!aws) {
812        aws = new AW_window_simple;
813        aws->init(aw_root, "CONSENSE_TREE", "Consensus Tree");
814        aws->load_xfig("ad_cons_tree.fig");
815
816        aws->auto_space(10, 10);
817
818        aws->callback(AW_POPDOWN);
819        aws->at("close");
820        aws->create_button("CLOSE", "CLOSE", "C");
821
822        aws->callback(makeHelpCallback("consense_tree.hlp"));
823        aws->at("help");
824        aws->create_button("HELP", "HELP", "H");
825
826        aws->at("list");
827        AW_DB_selection *all_trees      = awt_create_selection_list_on_trees(GLOBAL.gb_main, aws, AWAR_TREE_CONSENSE_SELECTED, true);
828        AW_selection    *selected_trees = awt_create_subset_selection_list(aws, all_trees->get_sellist(), "selected", "add", "sort");
829
830        aws->at("name");
831        aws->create_input_field(AWAR_TREE_CONSENSE_TREE);
832
833        aws->callback(use_selected_as_target_cb);
834        aws->create_button("USE_AS_TARGET", "#moveLeft.xpm", 0);
835
836        aws->at("build");
837        aws->callback(create_consense_tree_cb, AW_CL(selected_trees));
838        aws->create_autosize_button("BUILD", "Build consensus tree", "B");
839    }
840    return aws;
841}
842
843class PosInfo {
844    // describes relative position of a given subtree in a tree
845
846    double relpos_sum; // sum of all involved relative positions
847    size_t count;      // amount of positions involved
848
849    PosInfo() : relpos_sum(0), count(0) {} // = no position
850public:
851
852    static PosInfo unknown() { return PosInfo(); }
853
854    explicit PosInfo(double relpos_sum_) // create leaf-info
855        : relpos_sum(relpos_sum_),
856          count(1)
857    {}
858    PosInfo(const PosInfo& p1, const PosInfo& p2)
859        : relpos_sum(p1.relpos_sum + p2.relpos_sum),
860          count(p1.count + p2.count)
861    {}
862
863    bool is_known() const { return count; }
864    double value() const {
865        nt_assert(is_known());
866        return relpos_sum / count;
867    }
868    int compare(const PosInfo &right) const {
869        return double_cmp(value(), right.value());
870    }
871};
872
873class CombinedPosInfo {
874    // combines relative positions of a subtree in 2 trees (source- and target-tree).
875    // provides compare operations for SortByTopo
876
877    PosInfo source; // in source tree ("ordering tree")
878    PosInfo target; // in target tree ("modified tree")
879
880public:
881
882    CombinedPosInfo(const PosInfo& s, const PosInfo& t)
883        : source(s),
884          target(t)
885    {
886        nt_assert(target.is_known());
887    }
888    CombinedPosInfo(const CombinedPosInfo& c1, const CombinedPosInfo& c2)
889        : source(c1.source, c2.source),
890          target(c1.target, c2.target)
891    {}
892
893    int compare(const CombinedPosInfo &right) const {
894        // result similar to strcmp(this, right)
895        if (!source.is_known() || !right.source.is_known()) {
896            // one subtree is completely unknown in source-tree
897            // => keep target-tree order
898            return target.compare(right.target);
899        }
900        return source.compare(right.source);
901    }
902};
903
904
905class SpeciesPosition {
906    // provides relative position of species inside a tree
907
908    typedef std::map<std::string, unsigned> PosMap;
909    PosMap spos;
910
911    void fillFromTree(const GBT_TREE *node) {
912        if (node->is_leaf) {
913            size_t pos       = spos.size();
914            spos[node->name] = pos;
915        }
916        else {
917            fillFromTree(node->get_leftson());
918            fillFromTree(node->get_rightson());
919        }
920    }
921public:
922    explicit SpeciesPosition(const GBT_TREE *tree) {
923        fillFromTree(tree);
924    }
925
926    PosInfo relative(const char *name) const {
927        /*! returns PosInfo of species 'name' inside tree.
928         */
929        PosMap::const_iterator found = spos.find(name);
930        return (found == spos.end()) ? PosInfo::unknown() : PosInfo(found->second/double(spos.size()-1));
931    }
932};
933
934
935class SortByTopo : virtual Noncopyable {
936    SpeciesPosition        source_pos; // in ordering topology
937    const SpeciesPosition *target_pos; // in target topology (used where source_pos does not provide order)
938
939    CombinedPosInfo reorder_subtree_rec(RootedTree *node) { // similar to ../SL/ROOTED_TREE/RootedTree.cxx@reorder_subtree
940        static const char *smallest_leafname; // has to be set to the alphabetically smallest name (when function exits)
941
942        if (node->is_leaf) {
943            smallest_leafname = node->name;
944            return CombinedPosInfo(source_pos.relative(node->name),
945                                   target_pos->relative(node->name));
946        }
947
948        CombinedPosInfo  leftInfo       = reorder_subtree_rec(node->get_leftson());
949        const char      *smallest_left  = smallest_leafname;
950        CombinedPosInfo  rightInfo      = reorder_subtree_rec(node->get_rightson());
951        const char      *smallest_right = smallest_leafname;
952
953        bool left_leafname_bigger = strcmp(smallest_left, smallest_right)>0;
954        smallest_leafname         = left_leafname_bigger ? smallest_right : smallest_left;
955
956        {
957            int cmp = leftInfo.compare(rightInfo);
958            if (cmp>0 || (cmp == 0 && left_leafname_bigger)) {
959                node->swap_sons();
960            }
961        }
962
963        return CombinedPosInfo(leftInfo, rightInfo);
964    }
965public:
966
967    SortByTopo(const GBT_TREE *by)
968        : source_pos(by),
969          target_pos(NULL)
970    {}
971
972#if defined(UNIT_TESTS)
973    PosInfo sourcePos(const char *name) { return source_pos.relative(name); }
974#endif
975
976    void reorder_subtree(RootedTree *tree) {
977        SpeciesPosition tpos(tree);
978        LocallyModify<const SpeciesPosition*> provide(target_pos, &tpos);
979        reorder_subtree_rec(tree);
980    }
981};
982
983static GB_ERROR sort_tree_by_other_tree(GBDATA *gb_main, RootedTree *tree, const char *other_tree) {
984    GB_ERROR       error = NULL;
985    GB_transaction ta(gb_main);
986
987    GBT_TREE *otherTree   = GBT_read_tree(gb_main, other_tree, GBT_TREE_NodeFactory());
988    if (!otherTree) error = GB_await_error();
989    else {
990        SortByTopo sorter(otherTree);
991        delete otherTree;
992        sorter.reorder_subtree(tree);
993    }
994    return error;
995}
996
997static bool sort_dtree_by_other_tree_cb(RootedTree *tree, GB_ERROR& error) {
998    const char *other_tree = TreeAdmin::source_tree_awar(AW_root::SINGLETON)->read_char_pntr();
999    error = sort_tree_by_other_tree(GLOBAL.gb_main, tree, other_tree);
1000    return !error;
1001}
1002
1003static void sort_tree_by_other_tree_cb(UNFIXED, AWT_canvas *ntw) {
1004    GB_ERROR error = NT_with_displayed_tree_do(ntw, sort_dtree_by_other_tree_cb);
1005    aw_message_if(error);
1006}
1007
1008AW_window *NT_create_sort_tree_by_other_tree_window(AW_root *aw_root, AWT_canvas *ntw) {
1009    AW_window_simple *aws = create_select_other_tree_window(aw_root, ntw->aww->local_id("SORT_BY_OTHER"), "Sort tree by other tree", "resortbyother.hlp", ntw->user_awar);
1010
1011    aws->callback(makeWindowCallback(sort_tree_by_other_tree_cb, ntw));
1012    aws->create_autosize_button("RESORT", "Sort according to source tree");
1013
1014    return aws;
1015}
1016
1017// ---------------------------
1018//      multifurcate tree
1019
1020#define AWAR_MFURC                    "tree/multifurc/"
1021#define AWAR_MFURC_CONSIDER_BOOTSTRAP AWAR_MFURC "use_bs"
1022#define AWAR_MFURC_CONSIDER_LENGTH    AWAR_MFURC "use_len"
1023#define AWAR_MFURC_CONSIDER_TERMINALS AWAR_MFURC "terminals"
1024#define AWAR_MFURC_LENGTH_LIMIT       AWAR_MFURC "len"
1025#define AWAR_MFURC_BOOTSTRAP_LIMIT    AWAR_MFURC "bs"
1026
1027void NT_create_multifurcate_tree_awars(AW_root *aw_root, AW_default props) {
1028    aw_root->awar_int  (AWAR_MFURC_CONSIDER_BOOTSTRAP, 0,   props);
1029    aw_root->awar_int  (AWAR_MFURC_CONSIDER_LENGTH,    1,   props);
1030    aw_root->awar_int  (AWAR_MFURC_CONSIDER_TERMINALS, 0,   props);
1031    aw_root->awar_float(AWAR_MFURC_LENGTH_LIMIT,       0.1, props);
1032    aw_root->awar_float(AWAR_MFURC_BOOTSTRAP_LIMIT,    50,  props);
1033}
1034static void multifurcation_cb(UNFIXED, AWT_canvas *ntw) {
1035    AW_root *aw_root = ntw->aww->get_root();
1036
1037    double below_bootstrap = 101.0;
1038    double below_length    = 1000000.0;
1039    bool   applyAtLeafs    = aw_root->awar(AWAR_MFURC_CONSIDER_TERMINALS)->read_int();
1040
1041    if (aw_root->awar(AWAR_MFURC_CONSIDER_BOOTSTRAP)->read_int()) below_bootstrap = aw_root->awar(AWAR_MFURC_BOOTSTRAP_LIMIT)->read_float();
1042    if (aw_root->awar(AWAR_MFURC_CONSIDER_LENGTH)   ->read_int()) below_length    = aw_root->awar(AWAR_MFURC_LENGTH_LIMIT)   ->read_float();
1043
1044    NT_multifurcate_tree(ntw, RootedTree::multifurc_limits(below_bootstrap, below_length, applyAtLeafs));
1045}
1046AW_window *NT_create_multifurcate_tree_window(AW_root *aw_root, AWT_canvas *ntw) {
1047    AW_window_simple *aws = new AW_window_simple;
1048
1049    aws->init(aw_root, ntw->aww->local_id("multifurcate"), "Multifurcate tree");
1050    aws->at(10, 10);
1051    aws->auto_space(10, 10);
1052
1053    aws->callback((AW_CB0) AW_POPDOWN);
1054    aws->create_button("CLOSE", "CLOSE", "C");
1055
1056    aws->callback(makeHelpCallback("multifurcate.hlp"));
1057    aws->create_button("HELP", "HELP", "H");
1058
1059    const int LABEL_LENGTH = 46;
1060    aws->label_length(LABEL_LENGTH);
1061
1062    aws->at_newline();
1063    aws->label("Multifurcate branches with branchlength below");
1064    aws->create_toggle(AWAR_MFURC_CONSIDER_LENGTH);
1065    aws->create_input_field(AWAR_MFURC_LENGTH_LIMIT, 10);
1066
1067    aws->at_newline();
1068    aws->label("                          AND bootstrap below");
1069    aws->create_toggle(AWAR_MFURC_CONSIDER_BOOTSTRAP);
1070    aws->create_input_field(AWAR_MFURC_BOOTSTRAP_LIMIT, 10);
1071
1072    aws->label_length(0);
1073    aws->at_newline();
1074    aws->label("Also apply to terminal branches");
1075    aws->create_toggle(AWAR_MFURC_CONSIDER_TERMINALS);
1076
1077    aws->at_newline();
1078    aws->callback(makeWindowCallback(multifurcation_cb, ntw));
1079    aws->create_autosize_button("MULTIFURCATE", "Multifurcate", "M");
1080
1081    return aws;
1082}
1083
1084// --------------------------------------------------------------------------------
1085
1086#ifdef UNIT_TESTS
1087#ifndef TEST_UNIT_H
1088#include <test_unit.h>
1089#endif
1090
1091static GB_ERROR sort_namedtree_by_other_tree(GBDATA *gb_main, const char *tree, const char *other_tree) {
1092    GB_ERROR       error = NULL;
1093    GB_transaction ta(gb_main);
1094    SizeAwareTree *Tree = DOWNCAST(SizeAwareTree*, GBT_read_tree(gb_main, tree, *new TreeRoot(new SizeAwareNodeFactory, true)));
1095    if (!Tree) error = GB_await_error();
1096    else {
1097        Tree->compute_tree();
1098        error             = sort_tree_by_other_tree(gb_main, Tree, other_tree);
1099        if (!error) error = GBT_write_tree(gb_main, tree, Tree);
1100    }
1101    delete Tree;
1102    return error;
1103}
1104
1105void TEST_sort_tree_by_other_tree() {
1106    GB_shell  shell;
1107    GBDATA   *gb_main = GB_open("TEST_trees.arb", "rw");
1108    TEST_REJECT_NULL(gb_main);
1109
1110    const char *topo_test   = "(((((((CloTyro3:1.046,CloTyro4:0.061):0.026,CloTyro2:0.017):0.017,CloTyrob:0.009):0.274,CloInnoc:0.371):0.057,CloBifer:0.388):0.124,(((CloButy2:0.009,CloButyr:0.000):0.564,CloCarni:0.120):0.010,CloPaste:0.179):0.131):0.081,((((CorAquat:0.084,CurCitre:0.058):0.103,CorGluta:0.522):0.053,CelBiazo:0.059):0.207,CytAquat:0.711):0.081);";
1111    const char *topo_center = "(((CloPaste:0.179,((CloButy2:0.009,CloButyr:0.000):0.564,CloCarni:0.120):0.010):0.131,((CloInnoc:0.371,((CloTyro2:0.017,(CloTyro3:1.046,CloTyro4:0.061):0.026):0.017,CloTyrob:0.009):0.274):0.057,CloBifer:0.388):0.124):0.081,((CelBiazo:0.059,((CorAquat:0.084,CurCitre:0.058):0.103,CorGluta:0.522):0.053):0.207,CytAquat:0.711):0.081);";
1112    const char *topo_bottom = "((CytAquat:0.711,(CelBiazo:0.059,(CorGluta:0.522,(CorAquat:0.084,CurCitre:0.058):0.103):0.053):0.207):0.081,((CloPaste:0.179,(CloCarni:0.120,(CloButy2:0.009,CloButyr:0.000):0.564):0.010):0.131,(CloBifer:0.388,(CloInnoc:0.371,(CloTyrob:0.009,(CloTyro2:0.017,(CloTyro3:1.046,CloTyro4:0.061):0.026):0.017):0.274):0.057):0.124):0.081);";
1113
1114    const char *topo_vs_nj_bs = "(((((((CloTyro3:1.046,CloTyro4:0.061):0.026,CloTyro2:0.017):0.017,CloTyrob:0.009):0.274,CloInnoc:0.371):0.057,CloBifer:0.388):0.124,(((CloButyr:0.000,CloButy2:0.009):0.564,CloCarni:0.120):0.010,CloPaste:0.179):0.131):0.081,(((CorGluta:0.522,(CorAquat:0.084,CurCitre:0.058):0.103):0.053,CelBiazo:0.059):0.207,CytAquat:0.711):0.081);";
1115
1116    TEST_EXPECT_DIFFERENT(topo_test,   topo_center);
1117    TEST_EXPECT_DIFFERENT(topo_test,   topo_bottom);
1118    TEST_EXPECT_DIFFERENT(topo_center, topo_bottom);
1119
1120    // create sorted copies of tree_test
1121    {
1122        GB_transaction  ta(gb_main);
1123        SizeAwareTree  *tree = DOWNCAST(SizeAwareTree*, GBT_read_tree(gb_main, "tree_test", *new TreeRoot(new SizeAwareNodeFactory, true)));
1124        TEST_REJECT_NULL(tree);
1125        TEST_EXPECT_NEWICK(nLENGTH, tree, topo_test);
1126
1127        tree->reorder_tree(BIG_BRANCHES_TO_CENTER); TEST_EXPECT_NO_ERROR(GBT_write_tree(gb_main, "tree_sorted_center", tree)); TEST_EXPECT_NEWICK(nLENGTH, tree, topo_center);
1128        tree->reorder_tree(BIG_BRANCHES_TO_BOTTOM); TEST_EXPECT_NO_ERROR(GBT_write_tree(gb_main, "tree_sorted_bottom", tree)); TEST_EXPECT_NEWICK(nLENGTH, tree, topo_bottom);
1129
1130        // test SortByTopo
1131        {
1132            SortByTopo   sbt(tree);
1133            const double EPSILON = 0.0001;
1134
1135            TEST_EXPECT_SIMILAR(sbt.sourcePos("CytAquat").value(), 0.0, EPSILON); // leftmost species (in topo_bottom)
1136            TEST_EXPECT_SIMILAR(sbt.sourcePos("CloTyro4").value(), 1.0, EPSILON); // rightmost species
1137
1138            TEST_EXPECT_SIMILAR(sbt.sourcePos("CurCitre").value(), 0.2857, EPSILON); // (5 of 15)
1139            TEST_EXPECT_SIMILAR(sbt.sourcePos("CloButy2").value(), 0.5,    EPSILON); // center species (8 of 15)
1140            TEST_EXPECT_SIMILAR(sbt.sourcePos("CloTyrob").value(), 0.7857, EPSILON); // (12 of 15)
1141
1142            TEST_REJECT(sbt.sourcePos("Un-Known").is_known()); // unknown species
1143        }
1144
1145        tree->reorder_tree(BIG_BRANCHES_TO_EDGE); TEST_EXPECT_NO_ERROR(GBT_write_tree(gb_main, "tree_work", tree));
1146
1147        delete tree;
1148    }
1149
1150
1151    TEST_EXPECT_NO_ERROR(sort_namedtree_by_other_tree(gb_main, "tree_work", "tree_sorted_center")); TEST_EXPECT_SAVED_NEWICK(nLENGTH, gb_main, "tree_work", topo_center);
1152    TEST_EXPECT_NO_ERROR(sort_namedtree_by_other_tree(gb_main, "tree_work", "tree_sorted_bottom")); TEST_EXPECT_SAVED_NEWICK(nLENGTH, gb_main, "tree_work", topo_bottom);
1153    TEST_EXPECT_NO_ERROR(sort_namedtree_by_other_tree(gb_main, "tree_work", "tree_test"));          TEST_EXPECT_SAVED_NEWICK(nLENGTH, gb_main, "tree_work", topo_test);
1154    TEST_EXPECT_NO_ERROR(sort_namedtree_by_other_tree(gb_main, "tree_work", "tree_nj_bs"));         TEST_EXPECT_SAVED_NEWICK(nLENGTH, gb_main, "tree_work", topo_vs_nj_bs);
1155
1156    // TEST_EXPECT_NO_ERROR(GB_save_as(gb_main, "TEST_trees_save.arb", "b")); // test-save db to examine tree (do not commit)
1157
1158    GB_close(gb_main);
1159}
1160
1161void TEST_move_node_info() {
1162    GB_shell  shell;
1163    GBDATA   *gb_main = GB_open("TEST_trees.arb", "r");
1164
1165#define GROUP_TEST "(CloTyrob,(CloTyro2,(CloTyro3,CloTyro4)))"
1166
1167#define NAMED_GROUP_TEST       GROUP_TEST "'test'"
1168#define OVERWRITTEN_GROUP_TEST GROUP_TEST "'g2 [was: test]'"
1169
1170    const char *org_topo = "((CloInnoc," GROUP_TEST "),(CloBifer,((CloCarni,CurCitre),((CloPaste,(Zombie1,(CloButy2,CloButyr))),(CytAquat,(CelBiazo,(CorGluta,(CorAquat,Zombie2))))))));";
1171
1172    const char *unwanted_topo1 = "((CytAquat,(CelBiazo,(CorGluta,(CorAquat,Zombie2)))),((CloPaste,(Zombie1,(CloButy2,CloButyr))),((CloCarni,CurCitre),(CloBifer,(CloInnoc," NAMED_GROUP_TEST ")))));";
1173    const char *unwanted_topo2 = "((CloButy2,CloButyr),(Zombie1,(CloPaste,((((CloInnoc," OVERWRITTEN_GROUP_TEST "),CloBifer),(CloCarni,CurCitre)),(CytAquat,(CelBiazo,(CorGluta,(CorAquat,Zombie2)))))))'outer');";
1174
1175    const char *sorted_topo1 = "(((((CloInnoc," NAMED_GROUP_TEST "),CloBifer),(CloCarni,CurCitre)),(CloPaste,(Zombie1,(CloButy2,CloButyr)))),(CytAquat,(CelBiazo,(CorGluta,(CorAquat,Zombie2)))));";
1176    const char *sorted_topo2 = "(((((((CloInnoc," OVERWRITTEN_GROUP_TEST "),CloBifer),(CloCarni,CurCitre)),(CytAquat,(CelBiazo,(CorGluta,(CorAquat,Zombie2))))),CloPaste),Zombie1)'outer',(CloButy2,CloButyr));";
1177
1178    const char *compared_topo = "(((((((CloInnoc,(CloTyrob,(CloTyro2,(CloTyro3,CloTyro4)))),CloBifer),(CloCarni,CurCitre)'# 2')'# 2',(CytAquat,(CelBiazo,(CorGluta,(CorAquat,Zombie2)'# 1')'# 1')'# 1')'# 1')'# 1',CloPaste),Zombie1),(CloButy2,CloButyr));";
1179
1180    const char *LOG = "move_node_info.log";
1181
1182    // create copy of 'tree_removal'
1183    {
1184        GB_transaction  ta(gb_main);
1185        GBT_TREE       *tree = GBT_read_tree(gb_main, "tree_removal", GBT_TREE_NodeFactory());
1186
1187        TEST_EXPECT_NEWICK(nSIMPLE, tree, org_topo);
1188        TEST_EXPECT_NO_ERROR(GBT_write_tree(gb_main, "tree_removal_copy", tree));
1189        delete tree;
1190    }
1191
1192    // move node info
1193    {
1194        TEST_EXPECT_NO_ERROR(AWT_move_info(gb_main, "tree_test", "tree_removal", LOG, TREE_INFO_COPY, false));
1195
1196        TEST_EXPECT_SAVED_NEWICK__BROKEN(nSIMPLE, gb_main, "tree_removal", org_topo); // @@@ moving node info modifies topology (might be necessary to insert groups)
1197        TEST_EXPECT_SAVED_NEWICK(nGROUP, gb_main, "tree_removal", unwanted_topo1);
1198
1199        // @@@ when we have a function to set the root according to another tree (#449),
1200        // use that function here. sorting tree after that, should again result in 'org_topo'!
1201
1202        TEST_EXPECT_NO_ERROR(sort_namedtree_by_other_tree(gb_main, "tree_removal", "tree_removal_copy"));
1203        TEST_EXPECT_SAVED_NEWICK(nGROUP, gb_main, "tree_removal", sorted_topo1);
1204    }
1205
1206    // add node info
1207    {
1208        TEST_EXPECT_NO_ERROR(AWT_move_info(gb_main, "tree_tree2", "tree_removal", LOG, TREE_INFO_ADD, false));
1209
1210        TEST_EXPECT_SAVED_NEWICK__BROKEN(nSIMPLE, gb_main, "tree_removal", org_topo); // @@@ moving node info modifies topology (might be necessary to insert groups)
1211        TEST_EXPECT_SAVED_NEWICK(nGROUP, gb_main, "tree_removal", unwanted_topo2);
1212
1213        // @@@ when we have a function to set the root according to another tree (#449),
1214        // use that function here. sorting tree after that, should again result in 'org_topo'!
1215
1216        TEST_EXPECT_NO_ERROR(sort_namedtree_by_other_tree(gb_main, "tree_removal", "tree_removal_copy"));
1217        TEST_EXPECT_SAVED_NEWICK(nGROUP, gb_main, "tree_removal", sorted_topo2);
1218    }
1219
1220    // compare node info
1221    {
1222        TEST_EXPECT_NO_ERROR(AWT_move_info(gb_main, "tree_test", "tree_removal", NULL, TREE_INFO_COMPARE, false));
1223        TEST_EXPECT_SAVED_NEWICK(nREMARK, gb_main, "tree_removal", compared_topo);
1224    }
1225
1226    GB_unlink(LOG);
1227    GB_close(gb_main);
1228}
1229
1230void TEST_edges() {
1231    GB_shell  shell;
1232    GBDATA   *gb_main = GB_open("TEST_trees.arb", "rw");
1233    TEST_REJECT_NULL(gb_main);
1234
1235    {
1236        GB_transaction  ta(gb_main);
1237        RootedTree     *tree = DOWNCAST(RootedTree*, GBT_read_tree(gb_main, "tree_test", *new TreeRoot(new SizeAwareNodeFactory, true)));
1238
1239        RootedTree *left  = tree->findLeafNamed("CloTyro3"); TEST_REJECT_NULL(left);
1240        RootedTree *node  = left->get_father();              TEST_REJECT_NULL(node);
1241        RootedTree *right = node->findLeafNamed("CloTyro4"); TEST_REJECT_NULL(right);
1242
1243        TEST_EXPECT(node == right->get_father());
1244        TEST_EXPECT(node->get_leftson()  == left);
1245        TEST_EXPECT(node->get_rightson() == right);
1246
1247        RootedTree *parent  = node->get_father();                TEST_REJECT_NULL(parent);
1248        RootedTree *brother = parent->findLeafNamed("CloTyro2"); TEST_REJECT_NULL(brother);
1249
1250        TEST_EXPECT(node->get_brother() == brother);
1251
1252        RootedTree *grandpa  = parent->get_father(); TEST_REJECT_NULL(grandpa);
1253
1254        // topology:
1255        //
1256        //            grandpa
1257        //              /
1258        //             /
1259        //            /
1260        //          parent
1261        //           /\              .
1262        //          /  \             .
1263        //         /    \            .
1264        //       node  brother
1265        //        /\                 .
1266        //       /  \                .
1267        //      /    \               .
1268        //    left right
1269
1270        // test next() and otherNext() for inner edge 'node->parent'
1271        {
1272            ARB_edge nodeUp = parentEdge(node);
1273
1274            TEST_EXPECT(node->is_leftson()); // if child is left son..
1275            TEST_EXPECT(nodeUp.next().dest()      == grandpa); // .. next() continues rootwards
1276            TEST_EXPECT(nodeUp.otherNext().dest() == brother);
1277
1278            ARB_edge brotherUp = parentEdge(brother);
1279
1280            TEST_EXPECT(brother->is_rightson());               // if child is right son..
1281            TEST_EXPECT(brotherUp.next().dest()      == node); // .. next() continues with other son
1282            TEST_EXPECT(brotherUp.otherNext().dest() == grandpa);
1283
1284            ARB_edge down = nodeUp.inverse();
1285
1286            TEST_EXPECT(down.next().dest()      == right); // next descends into right son
1287            TEST_EXPECT(down.otherNext().dest() == left);
1288
1289            ARB_edge toLeaf(node, left);
1290            TEST_EXPECT(toLeaf.at_leaf());
1291
1292            // both iterators should turn around at leaf:
1293            TEST_EXPECT(toLeaf.next().dest()      == node);
1294            TEST_EXPECT(toLeaf.otherNext().dest() == node);
1295
1296            // test adjacent_distance
1297            const double EPSILON = 0.000001;
1298
1299            const double NLEN = 0.025806;
1300            const double BLEN = 0.017316;
1301            const double PLEN = 0.017167;
1302            const double LLEN = 1.045690;
1303            const double RLEN = 0.060606;
1304
1305            TEST_EXPECT_SIMILAR(node->get_branchlength(),             NLEN, EPSILON);
1306            TEST_EXPECT_SIMILAR(nodeUp.length(),                      NLEN, EPSILON);
1307            TEST_EXPECT_SIMILAR(down.length(),                        NLEN, EPSILON);
1308            TEST_EXPECT_SIMILAR(nodeUp.length_or_adjacent_distance(), NLEN, EPSILON);
1309            TEST_EXPECT_SIMILAR(down.length_or_adjacent_distance(),   NLEN, EPSILON);
1310
1311            TEST_EXPECT_SIMILAR(brother->get_branchlength(), BLEN,      EPSILON);
1312            TEST_EXPECT_SIMILAR(parent ->get_branchlength(), PLEN,      EPSILON);
1313            TEST_EXPECT_SIMILAR(nodeUp.adjacent_distance(),  BLEN+PLEN, EPSILON);
1314
1315            TEST_EXPECT_SIMILAR(left ->get_branchlength(), LLEN,      EPSILON);
1316            TEST_EXPECT_SIMILAR(right->get_branchlength(), RLEN,      EPSILON);
1317            TEST_EXPECT_SIMILAR(down.adjacent_distance(),  LLEN+RLEN, EPSILON);
1318
1319            // modify lengths
1320            const double MOD_NLEN = 0.123456;
1321            const double MOD_LLEN = 0.246802;
1322
1323            toLeaf.set_length(MOD_LLEN);
1324            nodeUp.set_length(MOD_NLEN);
1325
1326            TEST_EXPECT_SIMILAR(toLeaf.length(), MOD_LLEN, EPSILON);
1327            TEST_EXPECT_SIMILAR(nodeUp.length(), MOD_NLEN, EPSILON);
1328            TEST_EXPECT_SIMILAR(down.length(),   MOD_NLEN, EPSILON);
1329        }
1330
1331        delete tree;
1332    }
1333
1334    GB_close(gb_main);
1335}
1336
1337void TEST_toggle_bootstraps100() {
1338    GB_shell  shell;
1339    GBDATA   *gb_main = GB_open("TEST_trees.arb", "rw");
1340    TEST_REJECT_NULL(gb_main);
1341
1342    {
1343        GB_transaction  ta(gb_main);
1344        RootedTree     *tree = DOWNCAST(RootedTree*, GBT_read_tree(gb_main, "tree_test", *new TreeRoot(new SizeAwareNodeFactory, true)));
1345        TEST_REJECT_NULL(tree);
1346
1347        const char *topo_org   = "(((((((CloTyro3,CloTyro4)'40%',CloTyro2)'0%',CloTyrob)'97%',CloInnoc)'0%',CloBifer)'53%',(((CloButy2,CloButyr)'100%',CloCarni)'33%',CloPaste)'97%')'100%',((((CorAquat,CurCitre)'100%',CorGluta)'17%',CelBiazo)'40%',CytAquat)'100%');";
1348        const char *topo_no100 = "(((((((CloTyro3,CloTyro4)'40%',CloTyro2)'0%',CloTyrob)'97%',CloInnoc)'0%',CloBifer)'53%',(((CloButy2,CloButyr)"    ",CloCarni)'33%',CloPaste)'97%')"    ",((((CorAquat,CurCitre)"    ",CorGluta)'17%',CelBiazo)'40%',CytAquat)"    ");";
1349        const char *topo_rem   = "(((((((CloTyro3,CloTyro4),CloTyro2),CloTyrob),CloInnoc),CloBifer),(((CloButy2,CloButyr),CloCarni),CloPaste)),((((CorAquat,CurCitre),CorGluta),CelBiazo),CytAquat));";
1350
1351        TEST_EXPECT_NEWICK(nREMARK, tree, topo_org);
1352
1353        tree->toggle_bootstrap100();
1354        TEST_EXPECT_NEWICK(nREMARK, tree, topo_no100);
1355
1356        tree->toggle_bootstrap100();
1357        TEST_EXPECT_NEWICK(nREMARK, tree, topo_org);
1358
1359        tree->remove_bootstrap();
1360        TEST_EXPECT_NEWICK(nREMARK, tree, topo_rem);
1361
1362        delete tree;
1363    }
1364
1365    GB_close(gb_main);
1366}
1367
1368void TEST_multifurcate_tree() {
1369    GB_shell  shell;
1370    GBDATA   *gb_main = GB_open("TEST_trees.arb", "rw");
1371    TEST_REJECT_NULL(gb_main);
1372
1373    const char *topo_test               = "(((((((CloTyro3:1.046,CloTyro4:0.061)'40%':0.026,CloTyro2:0.017)'0%':0.017,CloTyrob:0.009)'97%:test':0.274,CloInnoc:0.371)'0%':0.057,CloBifer:0.388)'53%':0.124,(((CloButy2:0.009,CloButyr:0.000)'100%':0.564,CloCarni:0.120)'33%':0.010,CloPaste:0.179)'97%':0.131)'100%':0.081,((((CorAquat:0.084,CurCitre:0.058)'100%':0.103,CorGluta:0.522)'17%':0.053,CelBiazo:0.059)'40%':0.207,CytAquat:0.711)'100%':0.081);";
1374    // changes                          = "                                                                                                    +0.307         -0.371     +0.064 "
1375    const char *topo_single             = "(((((((CloTyro3:1.046,CloTyro4:0.061)'40%':0.026,CloTyro2:0.017)'0%':0.017,CloTyrob:0.009)'97%:test':0.581,CloInnoc:0.000)'0%':0.121,CloBifer:0.388)'53%':0.124,(((CloButy2:0.009,CloButyr:0.000)'100%':0.564,CloCarni:0.120)'33%':0.010,CloPaste:0.179)'97%':0.131)'100%':0.081,((((CorAquat:0.084,CurCitre:0.058)'100%':0.103,CorGluta:0.522)'17%':0.053,CelBiazo:0.059)'40%':0.207,CytAquat:0.711)'100%':0.081);";
1376    const char *topo_bs_less_101_005    = "(((((((CloTyro3:1.098,CloTyro4:0.064)"   ":0.000,CloTyro2:0.000)"  ":0.000,CloTyrob:0.000)'97%:test':0.287,CloInnoc:0.371)'0%':0.057,CloBifer:0.388)'53%':0.124,(((CloButy2:0.000,CloButyr:0.000)'100%':0.578,CloCarni:0.121)"   ":0.000,CloPaste:0.181)'97%':0.132)'100%':0.081,((((CorAquat:0.084,CurCitre:0.058)'100%':0.103,CorGluta:0.522)'17%':0.053,CelBiazo:0.059)'40%':0.207,CytAquat:0.711)'100%':0.081);";
1377    const char *topo_bs_less_101_005_NT = "(((((((CloTyro3:1.078,CloTyro4:0.062)"   ":0.000,CloTyro2:0.018)"  ":0.000,CloTyrob:0.009)'97%:test':0.282,CloInnoc:0.371)'0%':0.057,CloBifer:0.388)'53%':0.124,(((CloButy2:0.009,CloButyr:0.000)'100%':0.570,CloCarni:0.121)"   ":0.000,CloPaste:0.181)'97%':0.132)'100%':0.081,((((CorAquat:0.084,CurCitre:0.058)'100%':0.103,CorGluta:0.522)'17%':0.053,CelBiazo:0.059)'40%':0.207,CytAquat:0.711)'100%':0.081);";
1378    const char *topo_bs_less_30_005     = "(((((((CloTyro3:1.046,CloTyro4:0.061)'40%':0.027,CloTyro2:0.018)"  ":0.000,CloTyrob:0.009)'97%:test':0.288,CloInnoc:0.371)'0%':0.057,CloBifer:0.388)'53%':0.124,(((CloButy2:0.009,CloButyr:0.000)'100%':0.564,CloCarni:0.120)'33%':0.010,CloPaste:0.179)'97%':0.131)'100%':0.081,((((CorAquat:0.084,CurCitre:0.058)'100%':0.103,CorGluta:0.522)'17%':0.053,CelBiazo:0.059)'40%':0.207,CytAquat:0.711)'100%':0.081);";
1379    const char *topo_bs_less_30         = "(((((((CloTyro3:1.046,CloTyro4:0.061)'40%':0.027,CloTyro2:0.018)"  ":0.000,CloTyrob:0.009)'97%:test':0.302,CloInnoc:0.390)"  ":0.000,CloBifer:0.407)'53%':0.131,(((CloButy2:0.009,CloButyr:0.000)'100%':0.564,CloCarni:0.120)'33%':0.010,CloPaste:0.179)'97%':0.131)'100%':0.081,((((CorAquat:0.084,CurCitre:0.058)'100%':0.109,CorGluta:0.554)"   ":0.000,CelBiazo:0.062)'40%':0.220,CytAquat:0.711)'100%':0.081);";
1380    const char *topo_all                = "(((((((CloTyro3:0.000,CloTyro4:0.000)"   ":0.000,CloTyro2:0.000)"  ":0.000,CloTyrob:0.000)'"  "test':0.000,CloInnoc:0.000)"  ":0.000,CloBifer:0.000)"   ":0.000,(((CloButy2:0.000,CloButyr:0.000)"    ":0.000,CloCarni:0.000)"   ":0.000,CloPaste:0.000)"   ":0.000)"    ":0.000,((((CorAquat:0.000,CurCitre:0.000)"    ":0.000,CorGluta:0.000)"   ":0.000,CelBiazo:0.000)"   ":0.000,CytAquat:0.000)"    ":0.000);";
1381
1382    const double STABLE_LENGTH = 5.362750;
1383    const double EPSILON       = 0.000001;
1384
1385    for (int test = 1; test<=6; ++test) {
1386        GB_transaction  ta(gb_main);
1387        RootedTree     *tree = DOWNCAST(RootedTree*, GBT_read_tree(gb_main, "tree_test", *new TreeRoot(new SizeAwareNodeFactory, true)));
1388
1389        TEST_REJECT_NULL(tree);
1390        if (test == 1) {
1391            TEST_EXPECT_NEWICK(nALL, tree, topo_test);
1392            TEST_EXPECT_SIMILAR(tree->sum_child_lengths(), STABLE_LENGTH, EPSILON);
1393        }
1394
1395        switch (test) {
1396            case 1:
1397                tree->multifurcate_whole_tree(RootedTree::multifurc_limits(101, 0.05, true));
1398                TEST_EXPECT_NEWICK(nALL, tree, topo_bs_less_101_005);
1399                TEST_EXPECT_SIMILAR(tree->sum_child_lengths(), STABLE_LENGTH, EPSILON);
1400                break;
1401            case 6:
1402                tree->multifurcate_whole_tree(RootedTree::multifurc_limits(101, 0.05, false));
1403                TEST_EXPECT_NEWICK(nALL, tree, topo_bs_less_101_005_NT);
1404                TEST_EXPECT_SIMILAR(tree->sum_child_lengths(), STABLE_LENGTH, EPSILON);
1405                break;
1406            case 2:
1407                tree->multifurcate_whole_tree(RootedTree::multifurc_limits(30, 0.05, true));
1408                TEST_EXPECT_NEWICK(nALL, tree, topo_bs_less_30_005);
1409                TEST_EXPECT_SIMILAR(tree->sum_child_lengths(), STABLE_LENGTH, EPSILON);
1410                break;
1411            case 3:
1412                tree->multifurcate_whole_tree(RootedTree::multifurc_limits(30, 1000, true));
1413                TEST_EXPECT_NEWICK(nALL, tree, topo_bs_less_30);
1414                TEST_EXPECT_SIMILAR(tree->sum_child_lengths(), STABLE_LENGTH, EPSILON);
1415                break;
1416            case 4:
1417                tree->multifurcate_whole_tree(RootedTree::multifurc_limits(101, 1000, true)); // multifurcate all
1418                TEST_EXPECT_NEWICK(nALL, tree, topo_all);
1419                TEST_EXPECT_SIMILAR(tree->sum_child_lengths(), 0.0, EPSILON);
1420                break;
1421            case 5: {
1422                RootedTree *CloInnoc = tree->findLeafNamed("CloInnoc");
1423                TEST_REJECT_NULL(CloInnoc);
1424
1425                parentEdge(CloInnoc).multifurcate();
1426                TEST_EXPECT_NEWICK(nALL, tree, topo_single);
1427
1428                TEST_EXPECT_SIMILAR(tree->sum_child_lengths(), STABLE_LENGTH, EPSILON);
1429                break;
1430            }
1431            default:
1432                nt_assert(0);
1433                break;
1434        }
1435
1436        delete tree;
1437    }
1438
1439    GB_close(gb_main);
1440}
1441
1442#endif // UNIT_TESTS
1443
1444// --------------------------------------------------------------------------------
1445
Note: See TracBrowser for help on using the repository browser.