source: tags/ms_r18q1/NTREE/ad_trees.cxx

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