source: tags/ms_r16q3/NTREE/ad_trees.cxx

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