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 | |
---|
48 | #define AWAR_GROUPXFER_SAV AWAR_TREE_SAV "groupxfer/" |
---|
49 | |
---|
50 | #define AWAR_GROUPXFER_SOURCE AWAR_GROUPXFER_SAV "restrict" |
---|
51 | #define AWAR_GROUPXFER_OVERWRITE_MODE AWAR_GROUPXFER_SAV "overwrite" |
---|
52 | #define AWAR_GROUPXFER_INGROUP_ABS AWAR_GROUPXFER_SAV "ingroup/abs" |
---|
53 | #define AWAR_GROUPXFER_INGROUP_REL AWAR_GROUPXFER_SAV "ingroup/rel" |
---|
54 | #define AWAR_GROUPXFER_INGROUP_LIM AWAR_GROUPXFER_SAV "ingroup/lim" |
---|
55 | #define AWAR_GROUPXFER_OUTGROUP_ABS AWAR_GROUPXFER_SAV "outgroup/abs" |
---|
56 | #define AWAR_GROUPXFER_OUTGROUP_REL AWAR_GROUPXFER_SAV "outgroup/rel" |
---|
57 | #define AWAR_GROUPXFER_OUTGROUP_LIM AWAR_GROUPXFER_SAV "outgroup/lim" |
---|
58 | #define AWAR_GROUPXFER_UNKNOWN_ABS AWAR_GROUPXFER_SAV "unknown" |
---|
59 | #define AWAR_GROUPXFER_KEELING AWAR_GROUPXFER_SAV "keeling" |
---|
60 | #define AWAR_GROUPXFER_ACI AWAR_GROUPXFER_SAV "aci" |
---|
61 | |
---|
62 | #define AWAR_TREE_EXPORT_FILEBASE AWAR_TREE_TMP "export_tree" |
---|
63 | #define AWAR_TREE_EXPORT_FILTER AWAR_TREE_EXPORT_FILEBASE "/filter" |
---|
64 | #define AWAR_TREE_EXPORT_NAME AWAR_TREE_EXPORT_FILEBASE "/file_name" |
---|
65 | |
---|
66 | #define AWAR_TREE_EXPORT_SAV AWAR_TREE_SAV "export_tree/" |
---|
67 | |
---|
68 | #define AWAR_TREE_EXPORT_FORMAT AWAR_TREE_EXPORT_SAV "format" |
---|
69 | #define AWAR_TREE_EXPORT_NDS AWAR_TREE_EXPORT_SAV "NDS" |
---|
70 | #define AWAR_TREE_EXPORT_INCLUDE_BOOTSTRAPS AWAR_TREE_EXPORT_SAV "bootstraps" |
---|
71 | #define AWAR_TREE_EXPORT_INCLUDE_BRANCHLENS AWAR_TREE_EXPORT_SAV "branchlens" |
---|
72 | #define AWAR_TREE_EXPORT_INCLUDE_GROUPNAMES AWAR_TREE_EXPORT_SAV "groupnames" |
---|
73 | #define AWAR_TREE_EXPORT_HIDE_FOLDED_GROUPS AWAR_TREE_EXPORT_SAV "hide_folded" |
---|
74 | #define AWAR_TREE_EXPORT_QUOTEMODE AWAR_TREE_EXPORT_SAV "quote_mode" |
---|
75 | #define AWAR_TREE_EXPORT_REPLACE AWAR_TREE_EXPORT_SAV "replace" |
---|
76 | |
---|
77 | |
---|
78 | #define AWAR_TREE_CONSENSE_TMP AWAR_TREE_TMP "consense/" |
---|
79 | #define AWAR_TREE_CONSENSE_SAV AWAR_TREE_SAV "consense/" |
---|
80 | |
---|
81 | #define AWAR_TREE_CONSENSE_TREE AWAR_TREE_CONSENSE_SAV "tree" |
---|
82 | #define AWAR_TREE_CONSENSE_SELECTED AWAR_TREE_CONSENSE_TMP "selected" |
---|
83 | |
---|
84 | static void tree_vars_callback(AW_root *aw_root) { |
---|
85 | // map tree awars to display database entries (security+comment) |
---|
86 | |
---|
87 | if (GLOBAL.gb_main) { |
---|
88 | GB_transaction ta(GLOBAL.gb_main); |
---|
89 | |
---|
90 | char *treename = aw_root->awar(AWAR_TREE_NAME)->read_string(); |
---|
91 | GBDATA *gb_tree = GBT_find_tree(GLOBAL.gb_main, treename); |
---|
92 | |
---|
93 | if (!gb_tree) { |
---|
94 | aw_root->awar(AWAR_TREE_SECURITY)->unmap(); |
---|
95 | aw_root->awar(AWAR_TREE_REM)->unmap(); |
---|
96 | } |
---|
97 | else { |
---|
98 | GBDATA *tree_prot = GB_search(gb_tree, "security", GB_FIND); |
---|
99 | if (!tree_prot) GBT_readOrCreate_int(gb_tree, "security", GB_read_security_write(gb_tree)); |
---|
100 | tree_prot = GB_search(gb_tree, "security", GB_INT); |
---|
101 | |
---|
102 | GBDATA *tree_rem = GB_search(gb_tree, "remark", GB_STRING); |
---|
103 | aw_root->awar(AWAR_TREE_SECURITY)->map(tree_prot); |
---|
104 | aw_root->awar(AWAR_TREE_REM) ->map(tree_rem); |
---|
105 | } |
---|
106 | |
---|
107 | // create default filename from tree-name: |
---|
108 | { |
---|
109 | char *suffix = aw_root->awar(AWAR_TREE_EXPORT_FILTER)->read_string(); |
---|
110 | char *fname = GBS_string_eval(treename, GBS_global_string("*=*1.%s:tree_*=*1", suffix)); |
---|
111 | |
---|
112 | aw_root->awar(AWAR_TREE_EXPORT_NAME)->write_string(fname); // create default file name |
---|
113 | |
---|
114 | free(fname); |
---|
115 | free(suffix); |
---|
116 | } |
---|
117 | |
---|
118 | free(treename); |
---|
119 | } |
---|
120 | } |
---|
121 | |
---|
122 | static void update_default_treename_cb(AW_root *aw_root) { |
---|
123 | // update import tree name depending on file name |
---|
124 | GB_transaction ta(GLOBAL.gb_main); |
---|
125 | |
---|
126 | char *treename = aw_root->awar(AWAR_TREE_IMPORT "/file_name")->read_string(); |
---|
127 | char *treename_nopath = strrchr(treename, '/'); |
---|
128 | |
---|
129 | if (treename_nopath) { |
---|
130 | ++treename_nopath; |
---|
131 | } |
---|
132 | else { |
---|
133 | treename_nopath = treename; |
---|
134 | } |
---|
135 | |
---|
136 | char *fname = GBS_string_eval(treename_nopath, "*.tree=tree_*1:*.ntree=tree_*1:*.xml=tree_*1:.=:-=_: =_"); |
---|
137 | aw_root->awar(AWAR_TREE_IMPORT "/tree_name")->write_string(fname); |
---|
138 | |
---|
139 | free(fname); |
---|
140 | free(treename); |
---|
141 | } |
---|
142 | |
---|
143 | static void ad_tree_set_security(AW_root *aw_root) { |
---|
144 | if (GLOBAL.gb_main) { |
---|
145 | GB_transaction ta(GLOBAL.gb_main); |
---|
146 | |
---|
147 | char *treename = aw_root->awar(AWAR_TREE_NAME)->read_string(); |
---|
148 | GBDATA *gb_tree = GBT_find_tree(GLOBAL.gb_main, treename); |
---|
149 | |
---|
150 | if (gb_tree) { |
---|
151 | long prot = aw_root->awar(AWAR_TREE_SECURITY)->read_int(); |
---|
152 | long old = GB_read_security_delete(gb_tree); |
---|
153 | |
---|
154 | GB_ERROR error = NULp; |
---|
155 | if (old != prot) { |
---|
156 | error = GB_write_security_delete(gb_tree, prot); |
---|
157 | if (!error) error = GB_write_security_write(gb_tree, prot); |
---|
158 | } |
---|
159 | aw_message_if(error); |
---|
160 | } |
---|
161 | free(treename); |
---|
162 | } |
---|
163 | } |
---|
164 | |
---|
165 | enum ExportTreeType { |
---|
166 | AD_TREE_EXPORT_FORMAT_NEWICK, |
---|
167 | AD_TREE_EXPORT_FORMAT_XML, |
---|
168 | AD_TREE_EXPORT_FORMAT_NEWICK_PRETTY, |
---|
169 | }; |
---|
170 | |
---|
171 | enum ExportNodeType { |
---|
172 | AD_TREE_EXPORT_NODE_SPECIES_NAME, |
---|
173 | AD_TREE_EXPORT_NODE_NDS |
---|
174 | }; |
---|
175 | |
---|
176 | static void update_filter_cb(AW_root *root) { |
---|
177 | const char *filter_type = NULp; |
---|
178 | |
---|
179 | switch (ExportTreeType(root->awar(AWAR_TREE_EXPORT_FORMAT)->read_int())) { |
---|
180 | case AD_TREE_EXPORT_FORMAT_XML: filter_type = "xml"; break; |
---|
181 | case AD_TREE_EXPORT_FORMAT_NEWICK: |
---|
182 | case AD_TREE_EXPORT_FORMAT_NEWICK_PRETTY: |
---|
183 | switch (ExportNodeType(root->awar(AWAR_TREE_EXPORT_NDS)->read_int())) { |
---|
184 | case AD_TREE_EXPORT_NODE_SPECIES_NAME: filter_type = "tree"; break; |
---|
185 | case AD_TREE_EXPORT_NODE_NDS: filter_type = "ntree"; break; |
---|
186 | default: nt_assert(0); break; |
---|
187 | } |
---|
188 | break; |
---|
189 | default: nt_assert(0); break; |
---|
190 | } |
---|
191 | |
---|
192 | nt_assert(filter_type); |
---|
193 | root->awar(AWAR_TREE_EXPORT_FILTER)->write_string(filter_type); |
---|
194 | } |
---|
195 | |
---|
196 | void create_trees_var(AW_root *aw_root, AW_default aw_def) { |
---|
197 | AW_awar *awar_tree_name = aw_root->awar_string(AWAR_TREE_NAME, NULp, aw_def)->set_srt(SRT_AUTOCORRECT_TREENAME); |
---|
198 | |
---|
199 | aw_root->awar_pointer(AWAR_GROUP, NULp, aw_def); |
---|
200 | |
---|
201 | TreeAdmin::create_awars(aw_root, aw_def, true); |
---|
202 | |
---|
203 | aw_root->awar_int (AWAR_TREE_SECURITY, 0, aw_def); |
---|
204 | aw_root->awar_string(AWAR_TREE_REM, NULp, aw_def); |
---|
205 | |
---|
206 | AW_create_fileselection_awars(aw_root, AWAR_TREE_EXPORT_FILEBASE, "", ".tree", "treefile"); |
---|
207 | aw_root->awar_int(AWAR_TREE_EXPORT_FORMAT, AD_TREE_EXPORT_FORMAT_NEWICK, aw_def)-> add_callback(update_filter_cb); |
---|
208 | aw_root->awar_int(AWAR_TREE_EXPORT_NDS, AD_TREE_EXPORT_NODE_SPECIES_NAME, aw_def)-> add_callback(update_filter_cb); |
---|
209 | |
---|
210 | aw_root->awar_int(AWAR_TREE_EXPORT_INCLUDE_BOOTSTRAPS, 0, aw_def); |
---|
211 | aw_root->awar_int(AWAR_TREE_EXPORT_INCLUDE_BRANCHLENS, 1, aw_def); |
---|
212 | aw_root->awar_int(AWAR_TREE_EXPORT_HIDE_FOLDED_GROUPS, 0, aw_def); |
---|
213 | aw_root->awar_int(AWAR_TREE_EXPORT_INCLUDE_GROUPNAMES, 1, aw_def); |
---|
214 | aw_root->awar_int(AWAR_TREE_EXPORT_QUOTEMODE, TREE_SINGLE_QUOTES, aw_def); // old default behavior |
---|
215 | aw_root->awar_int(AWAR_TREE_EXPORT_REPLACE, 0, aw_def); // old default behavior |
---|
216 | |
---|
217 | AW_create_fileselection_awars(aw_root, AWAR_TREE_IMPORT, "", ".tree", "treefile"); |
---|
218 | |
---|
219 | aw_root->awar_string(AWAR_TREE_IMPORT "/tree_name", "tree_", aw_def)->set_srt(SRT_AUTOCORRECT_TREENAME); |
---|
220 | |
---|
221 | aw_root->awar(AWAR_TREE_IMPORT "/file_name")->add_callback(update_default_treename_cb); |
---|
222 | awar_tree_name->add_callback(tree_vars_callback); |
---|
223 | awar_tree_name->map(AWAR_TREE); |
---|
224 | aw_root->awar(AWAR_TREE_SECURITY)->add_callback(ad_tree_set_security); |
---|
225 | |
---|
226 | aw_root->awar_int(AWAR_GROUPXFER_SOURCE, XFER_ALL_GROUPS, aw_def); |
---|
227 | aw_root->awar_int(AWAR_GROUPXFER_OVERWRITE_MODE, REMOVE_EXISTING_GROUPS, aw_def); |
---|
228 | |
---|
229 | aw_root->awar_float(AWAR_GROUPXFER_INGROUP_ABS, 1.0, aw_def); |
---|
230 | aw_root->awar_float(AWAR_GROUPXFER_OUTGROUP_ABS, 1.0, aw_def); |
---|
231 | aw_root->awar_float(AWAR_GROUPXFER_INGROUP_REL, 0.0, aw_def); |
---|
232 | aw_root->awar_float(AWAR_GROUPXFER_OUTGROUP_REL, 0.0, aw_def); |
---|
233 | aw_root->awar_float(AWAR_GROUPXFER_INGROUP_LIM, 0.0, aw_def); |
---|
234 | aw_root->awar_float(AWAR_GROUPXFER_OUTGROUP_LIM, 100.0, aw_def); |
---|
235 | aw_root->awar_float(AWAR_GROUPXFER_UNKNOWN_ABS, 0.0001, aw_def); |
---|
236 | aw_root->awar_float(AWAR_GROUPXFER_KEELING, 0.01, aw_def); |
---|
237 | |
---|
238 | aw_root->awar_string(AWAR_GROUPXFER_ACI, "", aw_def); |
---|
239 | |
---|
240 | aw_root->awar_string(AWAR_TREE_CONSENSE_TREE, "tree_consensus", aw_def)->set_srt(SRT_AUTOCORRECT_TREENAME); |
---|
241 | AW_awar *ctree_awar = aw_root->awar_string(AWAR_TREE_CONSENSE_SELECTED, "", aw_def); |
---|
242 | AWT_registerTreeAwarSimple(ctree_awar); |
---|
243 | |
---|
244 | update_filter_cb(aw_root); |
---|
245 | tree_vars_callback(aw_root); |
---|
246 | } |
---|
247 | |
---|
248 | static void tree_save_cb(AW_window *aww) { |
---|
249 | AW_root *aw_root = aww->get_root(); |
---|
250 | char *tree_name = aw_root->awar(AWAR_TREE_NAME)->read_string(); |
---|
251 | |
---|
252 | GB_ERROR error = NULp; |
---|
253 | |
---|
254 | if (!tree_name || !strlen(tree_name)) { |
---|
255 | error = "Please select a tree first"; |
---|
256 | } |
---|
257 | else { |
---|
258 | char *fname = aw_root->awar(AWAR_TREE_EXPORT_NAME)->read_string(); |
---|
259 | char *db_name = aw_root->awar(AWAR_DB_NAME)->read_string(); |
---|
260 | |
---|
261 | bool use_NDS = ExportNodeType(aw_root->awar(AWAR_TREE_EXPORT_NDS)->read_int()) == AD_TREE_EXPORT_NODE_NDS; |
---|
262 | ExportTreeType exportType = static_cast<ExportTreeType>(aw_root->awar(AWAR_TREE_EXPORT_FORMAT)->read_int()); |
---|
263 | |
---|
264 | SmartPtr<TreeLabeler> labeler; |
---|
265 | if (use_NDS) labeler = new NDS_Labeler(NDS_OUTPUT_LEAFTEXT); |
---|
266 | else labeler = new Node_ID_Labeler; |
---|
267 | |
---|
268 | switch (exportType) { |
---|
269 | case AD_TREE_EXPORT_FORMAT_XML: |
---|
270 | error = TREE_write_XML(GLOBAL.gb_main, db_name, tree_name, *labeler, |
---|
271 | aw_root->awar(AWAR_TREE_EXPORT_HIDE_FOLDED_GROUPS)->read_int(), |
---|
272 | fname); |
---|
273 | break; |
---|
274 | |
---|
275 | case AD_TREE_EXPORT_FORMAT_NEWICK: |
---|
276 | case AD_TREE_EXPORT_FORMAT_NEWICK_PRETTY: |
---|
277 | TREE_node_quoting quoteMode = TREE_node_quoting(aw_root->awar(AWAR_TREE_EXPORT_QUOTEMODE)->read_int()); |
---|
278 | if (aw_root->awar(AWAR_TREE_EXPORT_REPLACE)->read_int()) { |
---|
279 | quoteMode = TREE_node_quoting(quoteMode|TREE_FORCE_REPLACE); |
---|
280 | } |
---|
281 | |
---|
282 | error = TREE_write_Newick(GLOBAL.gb_main, tree_name, *labeler, |
---|
283 | aw_root->awar(AWAR_TREE_EXPORT_INCLUDE_BRANCHLENS)->read_int(), |
---|
284 | aw_root->awar(AWAR_TREE_EXPORT_INCLUDE_BOOTSTRAPS)->read_int(), |
---|
285 | aw_root->awar(AWAR_TREE_EXPORT_INCLUDE_GROUPNAMES)->read_int(), |
---|
286 | exportType == AD_TREE_EXPORT_FORMAT_NEWICK_PRETTY, |
---|
287 | quoteMode, |
---|
288 | fname); |
---|
289 | break; |
---|
290 | } |
---|
291 | |
---|
292 | free(db_name); |
---|
293 | free(fname); |
---|
294 | } |
---|
295 | |
---|
296 | aww->hide_or_notify(error); |
---|
297 | free(tree_name); |
---|
298 | |
---|
299 | AW_refresh_fileselection(aw_root, AWAR_TREE_EXPORT_FILEBASE); |
---|
300 | } |
---|
301 | |
---|
302 | static AWT_config_mapping_def tree_export_config_mapping[] = { |
---|
303 | { AWAR_TREE_EXPORT_FORMAT, "format" }, |
---|
304 | { AWAR_TREE_EXPORT_NDS, "nodetype" }, |
---|
305 | { AWAR_TREE_EXPORT_INCLUDE_BRANCHLENS, "lengths" }, |
---|
306 | { AWAR_TREE_EXPORT_INCLUDE_BOOTSTRAPS, "bootstraps" }, |
---|
307 | { AWAR_TREE_EXPORT_INCLUDE_GROUPNAMES, "groupnames" }, |
---|
308 | { AWAR_TREE_EXPORT_HIDE_FOLDED_GROUPS, "hidefolded" }, |
---|
309 | { AWAR_TREE_EXPORT_QUOTEMODE, "quotemode" }, |
---|
310 | { AWAR_TREE_EXPORT_REPLACE, "replacechars" }, |
---|
311 | |
---|
312 | { NULp, NULp } |
---|
313 | }; |
---|
314 | |
---|
315 | static AW_window *create_tree_export_window(AW_root *root) { |
---|
316 | AW_window_simple *aws = new AW_window_simple; |
---|
317 | aws->init(root, "SAVE_TREE", "TREE SAVE"); |
---|
318 | aws->load_xfig("tree_export.fig"); |
---|
319 | |
---|
320 | aws->at("close"); |
---|
321 | aws->callback(AW_POPDOWN); |
---|
322 | aws->create_button("CLOSE", "CLOSE", "C"); |
---|
323 | |
---|
324 | aws->at("help"); |
---|
325 | aws->callback(makeHelpCallback("tr_export.hlp")); |
---|
326 | aws->create_button("HELP", "HELP", "H"); |
---|
327 | |
---|
328 | AW_create_standard_fileselection(aws, AWAR_TREE_EXPORT_FILEBASE); |
---|
329 | |
---|
330 | aws->auto_space(10, 10); |
---|
331 | |
---|
332 | aws->at("user"); |
---|
333 | aws->create_option_menu(AWAR_TREE_EXPORT_FORMAT, true); |
---|
334 | aws->insert_option("NEWICK TREE FORMAT", "N", AD_TREE_EXPORT_FORMAT_NEWICK); |
---|
335 | aws->insert_option("NEWICK TREE FORMAT (pretty, but big)", "P", AD_TREE_EXPORT_FORMAT_NEWICK_PRETTY); |
---|
336 | aws->insert_option("ARB_XML TREE FORMAT", "X", AD_TREE_EXPORT_FORMAT_XML); |
---|
337 | aws->update_option_menu(); |
---|
338 | |
---|
339 | aws->at("user2"); |
---|
340 | aws->label("Nodetype"); |
---|
341 | aws->create_toggle_field(AWAR_TREE_EXPORT_NDS, 1); |
---|
342 | aws->insert_default_toggle("Species ID ('name')", "S", 0); |
---|
343 | aws->insert_toggle("NDS", "N", 1); |
---|
344 | aws->update_toggle_field(); |
---|
345 | |
---|
346 | aws->at_newline(); aws->label("Save branch lengths"); aws->create_toggle(AWAR_TREE_EXPORT_INCLUDE_BRANCHLENS); |
---|
347 | aws->at_newline(); aws->label("Save bootstrap values"); aws->create_toggle(AWAR_TREE_EXPORT_INCLUDE_BOOTSTRAPS); |
---|
348 | aws->at_newline(); aws->label("Save group names"); aws->create_toggle(AWAR_TREE_EXPORT_INCLUDE_GROUPNAMES); |
---|
349 | aws->at_newline(); aws->label("Hide folded groups (XML only)"); aws->create_toggle(AWAR_TREE_EXPORT_HIDE_FOLDED_GROUPS); |
---|
350 | |
---|
351 | aws->at_newline(); |
---|
352 | aws->label("Name quoting (Newick only)"); |
---|
353 | aws->create_option_menu(AWAR_TREE_EXPORT_QUOTEMODE, true); |
---|
354 | aws->insert_option("none", "n", TREE_DISALLOW_QUOTES); |
---|
355 | aws->insert_option("single", "s", TREE_SINGLE_QUOTES); |
---|
356 | aws->insert_option("double", "d", TREE_DOUBLE_QUOTES); |
---|
357 | aws->insert_option("single (forced)", "i", TREE_SINGLE_QUOTES|TREE_FORCE_QUOTES); |
---|
358 | aws->insert_option("double (forced)", "o", TREE_DOUBLE_QUOTES|TREE_FORCE_QUOTES); |
---|
359 | aws->update_option_menu(); |
---|
360 | |
---|
361 | aws->at_newline(); aws->label("Replace problem chars"); aws->create_toggle(AWAR_TREE_EXPORT_REPLACE); |
---|
362 | |
---|
363 | aws->at_newline(); |
---|
364 | aws->button_length(10); |
---|
365 | |
---|
366 | aws->callback(tree_save_cb); |
---|
367 | aws->create_button("SAVE", "SAVE", "o"); |
---|
368 | |
---|
369 | AWT_insert_config_manager(aws, AW_ROOT_DEFAULT, "tree_export", tree_export_config_mapping); |
---|
370 | |
---|
371 | aws->window_fit(); |
---|
372 | update_filter_cb(root); |
---|
373 | |
---|
374 | return aws; |
---|
375 | } |
---|
376 | |
---|
377 | static char *readXmlTree(char *fname) { |
---|
378 | // create a temp file |
---|
379 | char tempFile[] = "newickXXXXXX"; |
---|
380 | int createTempFile = mkstemp(tempFile); |
---|
381 | |
---|
382 | if (createTempFile) { |
---|
383 | GBS_strstruct buf(strlen(fname)); |
---|
384 | |
---|
385 | // extract path from fname in order to place a copy of dtd file required to validate xml file |
---|
386 | { |
---|
387 | char *tmpFname = ARB_strdup(fname); |
---|
388 | for (char *tok = strtok(tmpFname, "/"); tok;) { |
---|
389 | char *tmp = tok; |
---|
390 | tok = strtok(NULp, "/"); |
---|
391 | if (tok) { |
---|
392 | buf.put('/'); |
---|
393 | buf.cat(tmp); |
---|
394 | } |
---|
395 | } |
---|
396 | free(tmpFname); |
---|
397 | } |
---|
398 | |
---|
399 | // linking arb_tree.dtd file to the Path from where xml file is loaded |
---|
400 | char *command = GBS_global_string_copy("ln -s %s/lib/dtd/arb_tree.dtd %s/.", GB_getenvARBHOME(), buf.get_data()); |
---|
401 | GB_xcmd(command, XCMD_SYNC_WAIT_ON_ERROR); |
---|
402 | |
---|
403 | // execute xml2newick to convert xml format tree to newick format tree |
---|
404 | command = GBS_global_string_copy("xml2newick %s %s", fname, tempFile); |
---|
405 | GB_xcmd(command, XCMD_SYNC_WAIT_ON_ERROR); |
---|
406 | |
---|
407 | free(command); |
---|
408 | |
---|
409 | // return newick format tree file |
---|
410 | return ARB_strdup(tempFile); |
---|
411 | } |
---|
412 | else { |
---|
413 | printf("Failed to create Temporary File to Parse xml file!\n"); |
---|
414 | return NULp; |
---|
415 | } |
---|
416 | } |
---|
417 | |
---|
418 | static void tree_load_cb(AW_window *aww) { |
---|
419 | GB_ERROR error = NULp; |
---|
420 | AW_root *aw_root = aww->get_root(); |
---|
421 | char *tree_name = aw_root->awar(AWAR_TREE_IMPORT "/tree_name")->read_string(); |
---|
422 | |
---|
423 | { |
---|
424 | char *pcTreeFormat = aw_root->awar(AWAR_TREE_IMPORT "/filter")->read_string(); |
---|
425 | char *fname = aw_root->awar(AWAR_TREE_IMPORT "/file_name")->read_string(); |
---|
426 | if (strcmp(pcTreeFormat, "xml") == 0) { |
---|
427 | char *tempFname = readXmlTree(fname); |
---|
428 | |
---|
429 | error = TREE_load_to_db(GLOBAL.gb_main, tempFname, tree_name); |
---|
430 | |
---|
431 | GB_unlink_or_warn(tempFname, NULp); |
---|
432 | free(tempFname); |
---|
433 | } |
---|
434 | else { |
---|
435 | error = TREE_load_to_db(GLOBAL.gb_main, fname, tree_name); |
---|
436 | } |
---|
437 | |
---|
438 | free(fname); |
---|
439 | free(pcTreeFormat); |
---|
440 | } |
---|
441 | |
---|
442 | aww->hide_or_notify(error); |
---|
443 | if (!error) aw_root->awar(AWAR_TREE)->write_string(tree_name); // show new tree |
---|
444 | |
---|
445 | free(tree_name); |
---|
446 | } |
---|
447 | |
---|
448 | static AW_window *create_tree_import_window(AW_root *root) { |
---|
449 | AW_window_simple *aws = new AW_window_simple; |
---|
450 | aws->init(root, "LOAD_TREE", "TREE LOAD"); |
---|
451 | aws->load_xfig("sel_box_tree.fig"); |
---|
452 | |
---|
453 | aws->at("close"); |
---|
454 | aws->callback(AW_POPDOWN); |
---|
455 | aws->create_button("CLOSE", "CLOSE", "C"); |
---|
456 | |
---|
457 | aws->at("help"); |
---|
458 | aws->callback(makeHelpCallback("tr_import.hlp")); |
---|
459 | aws->create_button("HELP", "HELP", "H"); |
---|
460 | |
---|
461 | aws->at("format"); |
---|
462 | aws->create_option_menu(AWAR_TREE_IMPORT "/filter", false); |
---|
463 | aws->insert_default_option("Newick", "t", "tree"); |
---|
464 | aws->insert_option("XML", "x", "xml"); |
---|
465 | aws->update_option_menu(); |
---|
466 | |
---|
467 | aws->at("user"); |
---|
468 | aws->label("Tree name"); |
---|
469 | aws->create_input_field(AWAR_TREE_IMPORT "/tree_name", 15); |
---|
470 | |
---|
471 | AW_create_standard_fileselection(aws, AWAR_TREE_IMPORT); |
---|
472 | |
---|
473 | aws->at("save2"); |
---|
474 | aws->callback(tree_load_cb); |
---|
475 | aws->create_button("LOAD", "LOAD", "o"); |
---|
476 | |
---|
477 | aws->window_fit(); |
---|
478 | |
---|
479 | return aws; |
---|
480 | } |
---|
481 | |
---|
482 | static void ad_move_tree_info(AW_window *aww, bool transferGroups) { |
---|
483 | AW_root *awr = aww->get_root(); |
---|
484 | |
---|
485 | char *log_file = NULp; |
---|
486 | GB_ERROR error = NULp; |
---|
487 | |
---|
488 | GroupTransferMode mode = COMPARE_TOPOLOGY; |
---|
489 | GroupsToTransfer what = XFER_ALL_GROUPS; |
---|
490 | |
---|
491 | if (transferGroups) { |
---|
492 | // log file is only written if transferring groups! |
---|
493 | char *log_name = GB_unique_filename("arb_node", "log"); |
---|
494 | log_file = GB_create_tempfile(log_name); |
---|
495 | if (!log_file) error = GB_await_error(); |
---|
496 | free(log_name); |
---|
497 | |
---|
498 | what = GroupsToTransfer(awr->awar(AWAR_GROUPXFER_SOURCE)->read_int()); |
---|
499 | mode = GroupTransferMode(awr->awar(AWAR_GROUPXFER_OVERWRITE_MODE)->read_int()); |
---|
500 | } |
---|
501 | |
---|
502 | if (!error) { |
---|
503 | char *src_tree = TreeAdmin::source_tree_awar(awr)->read_string(); |
---|
504 | char *dst_tree = TreeAdmin::dest_tree_awar(awr)->read_string(); |
---|
505 | char *aci = awr->awar(AWAR_GROUPXFER_ACI)->read_string(); |
---|
506 | |
---|
507 | NT_deselect_group(awr); // avoid crash (if group selected in target tree) |
---|
508 | |
---|
509 | GroupMatchScorer userScorer; |
---|
510 | if (transferGroups) { |
---|
511 | userScorer.setLimits(RatioLimits(awr->awar(AWAR_GROUPXFER_INGROUP_LIM)->read_float()/100, 1.0), |
---|
512 | RatioLimits(0.0, awr->awar(AWAR_GROUPXFER_OUTGROUP_LIM)->read_float()/100)); |
---|
513 | |
---|
514 | userScorer.setPerErrorPenalties(awr->awar(AWAR_GROUPXFER_INGROUP_ABS)->read_float(), |
---|
515 | awr->awar(AWAR_GROUPXFER_OUTGROUP_ABS)->read_float(), |
---|
516 | awr->awar(AWAR_GROUPXFER_UNKNOWN_ABS)->read_float()); |
---|
517 | |
---|
518 | userScorer.setRelativePenalties(awr->awar(AWAR_GROUPXFER_INGROUP_REL)->read_float(), |
---|
519 | awr->awar(AWAR_GROUPXFER_OUTGROUP_REL)->read_float()); |
---|
520 | } |
---|
521 | |
---|
522 | error = NTREE_move_tree_info(GLOBAL.gb_main, src_tree, dst_tree, log_file, mode, what, userScorer, aci); |
---|
523 | |
---|
524 | if (mode == COMPARE_TOPOLOGY && !error) { |
---|
525 | // if tree is not shown -> provide hint |
---|
526 | TREE_canvas *canvas_showing_dest = NT_get_canvas_showing_tree(dst_tree, false); |
---|
527 | if (!canvas_showing_dest) { |
---|
528 | aw_message(GBS_global_string("Note: annotations added to tree '%s'\n" |
---|
529 | "Press the right 'Display' button to view that tree", dst_tree)); |
---|
530 | } |
---|
531 | } |
---|
532 | |
---|
533 | if (log_file) { |
---|
534 | AW_edit(log_file); |
---|
535 | GB_remove_on_exit(log_file); |
---|
536 | } |
---|
537 | |
---|
538 | free(aci); |
---|
539 | free(dst_tree); |
---|
540 | free(src_tree); |
---|
541 | } |
---|
542 | |
---|
543 | aw_message_if(error); |
---|
544 | |
---|
545 | free(log_file); |
---|
546 | } |
---|
547 | |
---|
548 | static void swap_source_dest_cb(AW_window *aww) { |
---|
549 | AW_root *root = aww->get_root(); |
---|
550 | |
---|
551 | AW_awar *s = TreeAdmin::source_tree_awar(root); |
---|
552 | AW_awar *d = TreeAdmin::dest_tree_awar(root); |
---|
553 | |
---|
554 | char *old_src = s->read_string(); |
---|
555 | s->write_string(d->read_char_pntr()); |
---|
556 | d->write_string(old_src); |
---|
557 | free(old_src); |
---|
558 | } |
---|
559 | |
---|
560 | static void copy_tree_awar_cb(UNFIXED, AW_awar *aw_source, AW_awar *aw_dest) { |
---|
561 | const char *tree = aw_source->read_char_pntr(); |
---|
562 | if (tree && tree[0]) aw_dest->write_string(tree); |
---|
563 | } |
---|
564 | |
---|
565 | void NT_create_twoTreeSelection(AW_window *aws) { |
---|
566 | AW_root *root = aws->get_root(); |
---|
567 | |
---|
568 | aws->auto_space(10, 3); |
---|
569 | |
---|
570 | aws->at("tree1"); |
---|
571 | awt_create_TREE_selection_list(GLOBAL.gb_main, aws, TreeAdmin::source_tree_awar(root)->awar_name, true); |
---|
572 | aws->at("tree2"); |
---|
573 | awt_create_TREE_selection_list(GLOBAL.gb_main, aws, TreeAdmin::dest_tree_awar(root)->awar_name, false); |
---|
574 | |
---|
575 | AW_awar *awar_displayed_tree = root->awar(AWAR_TREE_NAME); |
---|
576 | |
---|
577 | { // let source tree default to currently displayed tree: |
---|
578 | static bool firstCall = true; |
---|
579 | if (firstCall) { |
---|
580 | TreeAdmin::source_tree_awar(root)->write_string(awar_displayed_tree->read_char_pntr()); |
---|
581 | firstCall = false; |
---|
582 | } |
---|
583 | } |
---|
584 | |
---|
585 | |
---|
586 | aws->at("select1"); |
---|
587 | aws->callback(makeWindowCallback(copy_tree_awar_cb, awar_displayed_tree, TreeAdmin::source_tree_awar(root))); aws->create_autosize_button("SELECT_DISPLAYED1", "Use shown"); |
---|
588 | aws->callback(makeWindowCallback(copy_tree_awar_cb, TreeAdmin::source_tree_awar(root), awar_displayed_tree)); aws->create_autosize_button("DISPLAY_SELECTED1", "Display"); |
---|
589 | |
---|
590 | aws->callback(swap_source_dest_cb); |
---|
591 | aws->create_autosize_button("SWAP", "Swap"); |
---|
592 | |
---|
593 | aws->at("select2"); |
---|
594 | aws->callback(makeWindowCallback(copy_tree_awar_cb, awar_displayed_tree, TreeAdmin::dest_tree_awar(root))); aws->create_autosize_button("SELECT_DISPLAYED2", "Use shown"); |
---|
595 | aws->callback(makeWindowCallback(copy_tree_awar_cb, TreeAdmin::dest_tree_awar(root), awar_displayed_tree)); aws->create_autosize_button("DISPLAY_SELECTED2", "Display"); |
---|
596 | } |
---|
597 | |
---|
598 | static 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) { |
---|
599 | AW_window_simple *aws = new AW_window_simple; |
---|
600 | aws->init(root, winId, winTitle); |
---|
601 | aws->load_xfig("ad_one_tree.fig"); |
---|
602 | |
---|
603 | aws->at("close"); |
---|
604 | aws->auto_space(10, 3); |
---|
605 | |
---|
606 | aws->callback(AW_POPDOWN); |
---|
607 | aws->create_button("CLOSE", "Close", "C"); |
---|
608 | |
---|
609 | aws->at("help"); |
---|
610 | aws->callback(makeHelpCallback(helpFile)); |
---|
611 | aws->create_button("HELP", "Help", "H"); |
---|
612 | |
---|
613 | AW_awar *awar_other_tree = TreeAdmin::dest_tree_awar(root); |
---|
614 | |
---|
615 | aws->at("tree"); |
---|
616 | awt_create_TREE_selection_list(GLOBAL.gb_main, aws, awar_other_tree->awar_name, true); |
---|
617 | |
---|
618 | aws->at("select"); |
---|
619 | aws->callback(makeWindowCallback(copy_tree_awar_cb, awar_displayed_tree, awar_other_tree)); aws->create_autosize_button("SELECT_DISPLAYED", "Use"); |
---|
620 | aws->callback(makeWindowCallback(copy_tree_awar_cb, awar_other_tree, awar_displayed_tree)); aws->create_autosize_button("DISPLAY_SELECTED", "Display"); |
---|
621 | |
---|
622 | aws->at("user"); |
---|
623 | |
---|
624 | return aws; |
---|
625 | } |
---|
626 | |
---|
627 | AW_window *NT_create_compareTopologies_window(AW_root *root) { |
---|
628 | AW_window_simple *aws = new AW_window_simple; |
---|
629 | aws->init(root, "CMP_TOPOLOGY", "Compare tree topologies"); |
---|
630 | aws->load_xfig("compare_topo.fig"); |
---|
631 | |
---|
632 | aws->at("close"); |
---|
633 | aws->callback(AW_POPDOWN); |
---|
634 | aws->create_button("CLOSE", "Close", "C"); |
---|
635 | |
---|
636 | aws->at("help"); |
---|
637 | aws->callback(makeHelpCallback("compare_topo.hlp")); |
---|
638 | aws->create_button("HELP", "Help", "H"); |
---|
639 | |
---|
640 | NT_create_twoTreeSelection(aws); |
---|
641 | |
---|
642 | aws->at("user"); |
---|
643 | aws->callback(makeWindowCallback(ad_move_tree_info, false)); |
---|
644 | aws->create_autosize_button("CMP_TOPOLOGY", "Compare topologies"); |
---|
645 | |
---|
646 | return aws; |
---|
647 | } |
---|
648 | |
---|
649 | static AWT_config_mapping_def moveGroupInfo_mapping[] = { |
---|
650 | { AWAR_GROUPXFER_INGROUP_ABS, "ingroup_abs" }, |
---|
651 | { AWAR_GROUPXFER_INGROUP_REL, "ingroup_rel" }, |
---|
652 | { AWAR_GROUPXFER_OUTGROUP_ABS, "outgroup_abs" }, |
---|
653 | { AWAR_GROUPXFER_OUTGROUP_REL, "outgroup_rel" }, |
---|
654 | |
---|
655 | { AWAR_GROUPXFER_UNKNOWN_ABS, "unknown_abs" }, |
---|
656 | { AWAR_GROUPXFER_KEELING, "keeling" }, |
---|
657 | |
---|
658 | { AWAR_GROUPXFER_INGROUP_LIM, "ingroup_lim" }, |
---|
659 | { AWAR_GROUPXFER_OUTGROUP_LIM, "outgroup_lim" }, |
---|
660 | |
---|
661 | { AWAR_GROUPXFER_SOURCE, "sourceGroups" }, |
---|
662 | { AWAR_GROUPXFER_OVERWRITE_MODE, "overwriteGroups" }, |
---|
663 | |
---|
664 | { AWAR_GROUPXFER_ACI, "aci" }, |
---|
665 | |
---|
666 | { NULp, NULp }, |
---|
667 | }; |
---|
668 | |
---|
669 | static AWT_predefined_config moveGroupInfo_predef[] = { |
---|
670 | { "*only_perfect_groups", "Only copy perfectly matching groups with\n * 100% ingroup ratio and\n * 0% outgroup ratio", "ingroup_lim='100';outgroup_lim='0'" }, |
---|
671 | |
---|
672 | { "*maximize_ingroup_ratio", "Maximize ingroup ratio \n over outgroup ratio.\n(Note: diff of factor 10 is maybe too strong)", "ingroup_abs='0';ingroup_rel='100';outgroup_abs='0';outgroup_rel='10'" }, |
---|
673 | { "*minimize_outgroup_ratio", "Minimize outgroup ratio \n over ingroup ratio.\n(Note: diff of factor 10 is maybe too strong)", "ingroup_abs='0';ingroup_rel='10';outgroup_abs='0';outgroup_rel='100'" }, |
---|
674 | |
---|
675 | { "*report2name", "custom target group name:\n * add prefix \"XFRD_\" (allows to distinguish newly transferred from existing groups)\n * add suffix reporting penalty", "aci='\"XFRD_\";groupname;\" {penalty = \";penalty;\"}\"'" }, |
---|
676 | |
---|
677 | { NULp, NULp, NULp }, |
---|
678 | }; |
---|
679 | |
---|
680 | |
---|
681 | |
---|
682 | AW_window *NT_create_moveGroupInfo_window(AW_root *root) { |
---|
683 | AW_window_simple *aws = new AW_window_simple; |
---|
684 | aws->init(root, "COPY_NODE_INFO_OF_TREE", "Move groups"); |
---|
685 | aws->load_xfig("move_groups.fig"); |
---|
686 | |
---|
687 | aws->button_length(11); |
---|
688 | |
---|
689 | aws->at("close"); |
---|
690 | aws->callback(AW_POPDOWN); |
---|
691 | aws->create_button("CLOSE", "Close", "C"); |
---|
692 | |
---|
693 | aws->at("help"); |
---|
694 | aws->callback(makeHelpCallback("move_groups.hlp")); |
---|
695 | aws->create_button("HELP", "Help", "H"); |
---|
696 | |
---|
697 | const int FLOAT_COLUMNS = 10; |
---|
698 | const int PERCENT_COLUMNS = 5; |
---|
699 | |
---|
700 | aws->at("ipep"); aws->create_input_field(AWAR_GROUPXFER_INGROUP_ABS, FLOAT_COLUMNS); |
---|
701 | aws->at("irp"); aws->create_input_field(AWAR_GROUPXFER_INGROUP_REL, FLOAT_COLUMNS); |
---|
702 | aws->at("ilim"); aws->create_input_field(AWAR_GROUPXFER_INGROUP_LIM, PERCENT_COLUMNS); |
---|
703 | |
---|
704 | aws->at("opep"); aws->create_input_field(AWAR_GROUPXFER_OUTGROUP_ABS, FLOAT_COLUMNS); |
---|
705 | aws->at("orp"); aws->create_input_field(AWAR_GROUPXFER_OUTGROUP_REL, FLOAT_COLUMNS); |
---|
706 | aws->at("olim"); aws->create_input_field(AWAR_GROUPXFER_OUTGROUP_LIM, PERCENT_COLUMNS); |
---|
707 | |
---|
708 | aws->at("upep"); aws->create_input_field(AWAR_GROUPXFER_UNKNOWN_ABS, FLOAT_COLUMNS); |
---|
709 | aws->at("keel"); aws->create_input_field(AWAR_GROUPXFER_KEELING, FLOAT_COLUMNS); |
---|
710 | |
---|
711 | aws->at("srcGrps"); |
---|
712 | aws->create_option_menu(AWAR_GROUPXFER_SOURCE, true); |
---|
713 | aws->insert_option("all groups", "a", XFER_ALL_GROUPS); |
---|
714 | aws->insert_option("groups with marked", "m", XFER_GROUPS_WITH_MARKED); |
---|
715 | aws->update_option_menu(); |
---|
716 | |
---|
717 | aws->at("tgtGrps"); |
---|
718 | aws->create_option_menu(AWAR_GROUPXFER_OVERWRITE_MODE, true); |
---|
719 | aws->insert_option("remove existing groups", "r", REMOVE_EXISTING_GROUPS); |
---|
720 | aws->insert_option("keep \"newname [was: oldname]\"", "k", KEEP_OLD_NAMES); |
---|
721 | aws->update_option_menu(); |
---|
722 | |
---|
723 | aws->at("aci"); |
---|
724 | aws->create_input_field(AWAR_GROUPXFER_ACI); |
---|
725 | |
---|
726 | NT_create_twoTreeSelection(aws); |
---|
727 | |
---|
728 | aws->at("go"); |
---|
729 | aws->callback(makeWindowCallback(ad_move_tree_info, true)); |
---|
730 | aws->create_button("GO", "GO", "G"); |
---|
731 | |
---|
732 | aws->at("cfg"); |
---|
733 | AWT_insert_config_manager(aws, AW_ROOT_DEFAULT, "moveGroupInfo", moveGroupInfo_mapping, NULp, moveGroupInfo_predef); |
---|
734 | |
---|
735 | return aws; |
---|
736 | } |
---|
737 | |
---|
738 | static void reorder_trees_cb(AW_window *aww, awt_reorder_mode dest) { |
---|
739 | // moves the tree in the list of trees |
---|
740 | |
---|
741 | char *tree_name = aww->get_root()->awar(AWAR_TREE_NAME)->read_string(); |
---|
742 | GB_ERROR error = NULp; |
---|
743 | |
---|
744 | GB_transaction ta(GLOBAL.gb_main); |
---|
745 | GBDATA *gb_treedata = GBT_get_tree_data(GLOBAL.gb_main); |
---|
746 | GBDATA *gb_moved_tree = GB_entry(gb_treedata, tree_name); |
---|
747 | |
---|
748 | if (!gb_moved_tree) { |
---|
749 | error = "No tree selected"; |
---|
750 | } |
---|
751 | else { |
---|
752 | GBT_ORDER_MODE move_mode; |
---|
753 | GBDATA *gb_target_tree = NULp; |
---|
754 | |
---|
755 | switch (dest) { |
---|
756 | case ARM_UP: |
---|
757 | move_mode = GBT_INFRONTOF; |
---|
758 | gb_target_tree = GBT_tree_infrontof(gb_moved_tree); |
---|
759 | if (gb_target_tree) break; |
---|
760 | FALLTHROUGH; // move top-tree up = move to bottom |
---|
761 | case ARM_BOTTOM: |
---|
762 | move_mode = GBT_BEHIND; |
---|
763 | gb_target_tree = GBT_find_bottom_tree(GLOBAL.gb_main); |
---|
764 | break; |
---|
765 | |
---|
766 | case ARM_DOWN: |
---|
767 | move_mode = GBT_BEHIND; |
---|
768 | gb_target_tree = GBT_tree_behind(gb_moved_tree); |
---|
769 | if (gb_target_tree) break; |
---|
770 | FALLTHROUGH; // move bottom-tree down = move to top |
---|
771 | case ARM_TOP: |
---|
772 | move_mode = GBT_INFRONTOF; |
---|
773 | gb_target_tree = GBT_find_top_tree(GLOBAL.gb_main); |
---|
774 | break; |
---|
775 | } |
---|
776 | |
---|
777 | if (gb_target_tree && gb_target_tree != gb_moved_tree) { |
---|
778 | error = GBT_move_tree(gb_moved_tree, move_mode, gb_target_tree); |
---|
779 | } |
---|
780 | } |
---|
781 | |
---|
782 | if (error) aw_message(error); |
---|
783 | free(tree_name); |
---|
784 | } |
---|
785 | |
---|
786 | void popup_tree_admin_window(AW_window *awp) { |
---|
787 | static AW_window_simple *aws = NULp; |
---|
788 | |
---|
789 | if (!aws) { |
---|
790 | AW_root *aw_root = awp->get_root(); |
---|
791 | |
---|
792 | aws = new AW_window_simple; |
---|
793 | aws->init(aw_root, "TREE_ADMIN", "TREE ADMIN"); |
---|
794 | aws->load_xfig("ad_tree.fig"); |
---|
795 | |
---|
796 | aws->callback(AW_POPDOWN); |
---|
797 | aws->at("close"); |
---|
798 | aws->create_button("CLOSE", "CLOSE", "C"); |
---|
799 | |
---|
800 | aws->callback(makeHelpCallback("treeadm.hlp")); |
---|
801 | aws->at("help"); |
---|
802 | aws->create_button("HELP", "HELP", "H"); |
---|
803 | |
---|
804 | aws->button_length(40); |
---|
805 | |
---|
806 | aws->at("sel"); |
---|
807 | aws->create_button(NULp, AWAR_TREE_NAME, NULp, "+"); |
---|
808 | |
---|
809 | aws->at("security"); |
---|
810 | aws->create_option_menu(AWAR_TREE_SECURITY, true); |
---|
811 | aws->insert_option("0", "0", 0); |
---|
812 | aws->insert_option("1", "1", 1); |
---|
813 | aws->insert_option("2", "2", 2); |
---|
814 | aws->insert_option("3", "3", 3); |
---|
815 | aws->insert_option("4", "4", 4); |
---|
816 | aws->insert_option("5", "5", 5); |
---|
817 | aws->insert_default_option("6", "6", 6); |
---|
818 | aws->update_option_menu(); |
---|
819 | |
---|
820 | aws->at("rem"); |
---|
821 | aws->create_text_field(AWAR_TREE_REM); |
---|
822 | |
---|
823 | |
---|
824 | aws->button_length(20); |
---|
825 | |
---|
826 | static TreeAdmin::Spec spec(GLOBAL.gb_main, AWAR_TREE_NAME); |
---|
827 | |
---|
828 | aws->at("delete"); |
---|
829 | aws->help_text("treeadm.hlp"); |
---|
830 | aws->callback(makeWindowCallback(TreeAdmin::delete_tree_cb, &spec)); |
---|
831 | aws->create_button("DELETE", "Delete", "D"); |
---|
832 | |
---|
833 | aws->at("rename"); |
---|
834 | aws->help_text("treeadm.hlp"); |
---|
835 | aws->callback(makeCreateWindowCallback(TreeAdmin::create_rename_window, &spec)); |
---|
836 | aws->create_button("RENAME", "Rename", "R"); |
---|
837 | |
---|
838 | aws->at("copy"); |
---|
839 | aws->help_text("treeadm.hlp"); |
---|
840 | aws->callback(makeCreateWindowCallback(TreeAdmin::create_copy_window, &spec)); |
---|
841 | aws->create_button("COPY", "Copy", "C"); |
---|
842 | |
---|
843 | aws->at("export"); |
---|
844 | aws->help_text("tr_export.hlp"); |
---|
845 | aws->callback(create_tree_export_window); |
---|
846 | aws->create_button("EXPORT", "Export", "E"); |
---|
847 | |
---|
848 | aws->at("import"); |
---|
849 | aws->help_text("tr_import.hlp"); |
---|
850 | aws->callback(create_tree_import_window); |
---|
851 | aws->create_button("IMPORT", "Import", "I"); |
---|
852 | |
---|
853 | aws->button_length(0); |
---|
854 | |
---|
855 | aws->at("list"); |
---|
856 | awt_create_TREE_selection_list(GLOBAL.gb_main, aws, AWAR_TREE_NAME, true); |
---|
857 | |
---|
858 | aws->at("sort"); |
---|
859 | awt_create_order_buttons(aws, reorder_trees_cb); |
---|
860 | } |
---|
861 | |
---|
862 | aws->activate(); |
---|
863 | } |
---|
864 | |
---|
865 | // ----------------------- |
---|
866 | // consense tree |
---|
867 | |
---|
868 | |
---|
869 | static void create_consense_tree_cb(AW_window *aww, AW_selection *selected_trees) { |
---|
870 | AW_root *aw_root = aww->get_root(); |
---|
871 | GB_ERROR error = NULp; |
---|
872 | |
---|
873 | const char *cons_tree_name = aw_root->awar(AWAR_TREE_CONSENSE_TREE)->read_char_pntr(); |
---|
874 | if (!cons_tree_name || !cons_tree_name[0]) { |
---|
875 | error = "No name specified for the consensus tree"; |
---|
876 | } |
---|
877 | else { |
---|
878 | StrArray tree_names; |
---|
879 | selected_trees->get_values(tree_names); |
---|
880 | |
---|
881 | if (tree_names.size()<2) { |
---|
882 | error = "Not enough trees selected (at least 2 needed)"; |
---|
883 | } |
---|
884 | else { |
---|
885 | GBDATA *gb_main = GLOBAL.gb_main; |
---|
886 | GB_transaction ta(gb_main); |
---|
887 | |
---|
888 | { |
---|
889 | arb_progress progress("Building consensus tree", 2L); // 2 steps: deconstruct, reconstruct |
---|
890 | ConsensusTreeBuilder tree_builder; |
---|
891 | |
---|
892 | progress.subtitle("loading input trees"); |
---|
893 | for (size_t t = 0; t<tree_names.size() && !error; ++t) { |
---|
894 | TreeRoot *root = new SizeAwareRoot; // will be deleted when tree gets deleted |
---|
895 | SizeAwareTree *tree = DOWNCAST(SizeAwareTree*, GBT_read_tree(gb_main, tree_names[t], root)); |
---|
896 | if (!tree) { |
---|
897 | error = GB_await_error(); |
---|
898 | } |
---|
899 | else { |
---|
900 | tree_builder.add(tree, tree_names[t], 1.0); |
---|
901 | } |
---|
902 | } |
---|
903 | |
---|
904 | if (!error) { |
---|
905 | size_t species_count; |
---|
906 | TreeNode *cons_tree = tree_builder.get(species_count, error); // triggers 2 implicit progress increments |
---|
907 | |
---|
908 | if (!error && progress.aborted()) { |
---|
909 | error = "user abort"; |
---|
910 | } |
---|
911 | |
---|
912 | nt_assert(contradicted(cons_tree, error)); |
---|
913 | if (cons_tree) { |
---|
914 | char *comment = tree_builder.get_tree_remark(); |
---|
915 | error = GBT_write_tree_with_remark(gb_main, cons_tree_name, cons_tree, comment); |
---|
916 | free(comment); |
---|
917 | UNCOVERED(); |
---|
918 | destroy(cons_tree); |
---|
919 | } |
---|
920 | } |
---|
921 | if (error) progress.done(); |
---|
922 | } |
---|
923 | error = ta.close(error); |
---|
924 | } |
---|
925 | } |
---|
926 | |
---|
927 | if (!error) { |
---|
928 | aw_root->awar(AWAR_TREE_NAME)->write_string(cons_tree_name); // show in main window |
---|
929 | } |
---|
930 | |
---|
931 | aw_message_if(error); |
---|
932 | } |
---|
933 | |
---|
934 | static void use_selected_as_target_cb(AW_window *aww) { |
---|
935 | AW_root *aw_root = aww->get_root(); |
---|
936 | aw_root->awar(AWAR_TREE_CONSENSE_TREE)->write_string(aw_root->awar(AWAR_TREE_CONSENSE_SELECTED)->read_char_pntr()); |
---|
937 | } |
---|
938 | |
---|
939 | AW_window *NT_create_consense_window(AW_root *aw_root) { |
---|
940 | static AW_window_simple *aws = NULp; |
---|
941 | if (!aws) { |
---|
942 | aws = new AW_window_simple; |
---|
943 | aws->init(aw_root, "CONSENSE_TREE", "Consensus Tree"); |
---|
944 | aws->load_xfig("ad_cons_tree.fig"); |
---|
945 | |
---|
946 | aws->auto_space(10, 10); |
---|
947 | |
---|
948 | aws->callback(AW_POPDOWN); |
---|
949 | aws->at("close"); |
---|
950 | aws->create_button("CLOSE", "CLOSE", "C"); |
---|
951 | |
---|
952 | aws->callback(makeHelpCallback("consense_tree.hlp")); |
---|
953 | aws->at("help"); |
---|
954 | aws->create_button("HELP", "HELP", "H"); |
---|
955 | |
---|
956 | aws->at("list"); |
---|
957 | AW_DB_selection *all_trees = awt_create_TREE_selection_list(GLOBAL.gb_main, aws, AWAR_TREE_CONSENSE_SELECTED, true); |
---|
958 | AW_selection *selected_trees = awt_create_subset_selection_list(aws, all_trees->get_sellist(), "selected", "add", "sort"); |
---|
959 | |
---|
960 | aws->at("name"); |
---|
961 | aws->create_input_field(AWAR_TREE_CONSENSE_TREE); |
---|
962 | |
---|
963 | aws->callback(use_selected_as_target_cb); |
---|
964 | aws->create_button("USE_AS_TARGET", "#moveLeft.xpm"); |
---|
965 | |
---|
966 | aws->at("build"); |
---|
967 | aws->callback(makeWindowCallback(create_consense_tree_cb, selected_trees)); |
---|
968 | aws->create_autosize_button("BUILD", "Build consensus tree", "B"); |
---|
969 | } |
---|
970 | return aws; |
---|
971 | } |
---|
972 | |
---|
973 | class CombinedPosInfo { |
---|
974 | // combines relative positions of a subtree in 2 trees (source- and target-tree). |
---|
975 | // provides compare operations for SortByTopo |
---|
976 | |
---|
977 | TreeRelativePosition source; // in source tree ("ordering tree") |
---|
978 | TreeRelativePosition target; // in target tree ("modified tree") |
---|
979 | |
---|
980 | public: |
---|
981 | |
---|
982 | CombinedPosInfo(const TreeRelativePosition& s, const TreeRelativePosition& t) |
---|
983 | : source(s), |
---|
984 | target(t) |
---|
985 | { |
---|
986 | nt_assert(target.is_known()); |
---|
987 | } |
---|
988 | CombinedPosInfo(const CombinedPosInfo& c1, const CombinedPosInfo& c2) |
---|
989 | : source(c1.source, c2.source), |
---|
990 | target(c1.target, c2.target) |
---|
991 | {} |
---|
992 | |
---|
993 | int compare(const CombinedPosInfo &right) const { |
---|
994 | // result similar to strcmp(this, right) |
---|
995 | if (!source.is_known() || !right.source.is_known()) { |
---|
996 | // one subtree is completely unknown in source-tree |
---|
997 | // => keep target-tree order |
---|
998 | return target.compare(right.target); |
---|
999 | } |
---|
1000 | return source.compare(right.source); |
---|
1001 | } |
---|
1002 | }; |
---|
1003 | |
---|
1004 | class SortByTopo : virtual Noncopyable { |
---|
1005 | TreePositionLookup source_pos; // in ordering topology |
---|
1006 | const TreePositionLookup *target_pos; // in target topology (used where source_pos does not provide order) |
---|
1007 | |
---|
1008 | CombinedPosInfo reorder_subtree_rec(TreeNode *node) { // similar to ../ARBDB/TreeNode.cxx@reorder_subtree |
---|
1009 | static const char *smallest_leafname; // has to be set to the alphabetically smallest name (when function exits) |
---|
1010 | |
---|
1011 | if (node->is_leaf()) { |
---|
1012 | smallest_leafname = node->name; |
---|
1013 | return CombinedPosInfo(source_pos.relative(node->name), |
---|
1014 | target_pos->relative(node->name)); |
---|
1015 | } |
---|
1016 | |
---|
1017 | CombinedPosInfo leftInfo = reorder_subtree_rec(node->get_leftson()); |
---|
1018 | const char *smallest_left = smallest_leafname; |
---|
1019 | CombinedPosInfo rightInfo = reorder_subtree_rec(node->get_rightson()); |
---|
1020 | const char *smallest_right = smallest_leafname; |
---|
1021 | |
---|
1022 | bool left_leafname_bigger = strcmp(smallest_left, smallest_right)>0; |
---|
1023 | smallest_leafname = left_leafname_bigger ? smallest_right : smallest_left; |
---|
1024 | |
---|
1025 | { |
---|
1026 | int cmp = leftInfo.compare(rightInfo); |
---|
1027 | if (cmp>0 || (cmp == 0 && left_leafname_bigger)) { |
---|
1028 | node->swap_sons(); |
---|
1029 | } |
---|
1030 | } |
---|
1031 | |
---|
1032 | return CombinedPosInfo(leftInfo, rightInfo); |
---|
1033 | } |
---|
1034 | public: |
---|
1035 | |
---|
1036 | SortByTopo(const TreeNode *by) |
---|
1037 | : source_pos(by), |
---|
1038 | target_pos(NULp) |
---|
1039 | {} |
---|
1040 | |
---|
1041 | #if defined(UNIT_TESTS) |
---|
1042 | TreeRelativePosition sourcePos(const char *name) { return source_pos.relative(name); } |
---|
1043 | #endif |
---|
1044 | |
---|
1045 | void reorder_subtree(TreeNode *tree) { |
---|
1046 | TreePositionLookup tpos(tree); |
---|
1047 | LocallyModify<const TreePositionLookup*> provide(target_pos, &tpos); |
---|
1048 | reorder_subtree_rec(tree); |
---|
1049 | } |
---|
1050 | }; |
---|
1051 | |
---|
1052 | static GB_ERROR sort_tree_by_other_tree(GBDATA *gb_main, TreeNode *tree, const char *other_tree) { |
---|
1053 | GB_ERROR error = NULp; |
---|
1054 | GB_transaction ta(gb_main); |
---|
1055 | |
---|
1056 | TreeNode *otherTree = GBT_read_tree(gb_main, other_tree, new SimpleRoot); |
---|
1057 | if (!otherTree) error = GB_await_error(); |
---|
1058 | else { |
---|
1059 | SortByTopo sorter(otherTree); |
---|
1060 | destroy(otherTree); |
---|
1061 | sorter.reorder_subtree(tree); |
---|
1062 | } |
---|
1063 | return error; |
---|
1064 | } |
---|
1065 | |
---|
1066 | static bool sort_dtree_by_other_tree_cb(TreeNode *tree, GB_ERROR& error) { |
---|
1067 | const char *other_tree = TreeAdmin::dest_tree_awar(AW_root::SINGLETON)->read_char_pntr(); |
---|
1068 | error = sort_tree_by_other_tree(GLOBAL.gb_main, tree, other_tree); |
---|
1069 | return !error; |
---|
1070 | } |
---|
1071 | |
---|
1072 | static void sort_tree_by_other_tree_cb(UNFIXED, TREE_canvas *ntw) { |
---|
1073 | GB_ERROR error = NT_with_displayed_tree_do(ntw, sort_dtree_by_other_tree_cb); |
---|
1074 | aw_message_if(error); |
---|
1075 | } |
---|
1076 | |
---|
1077 | AW_window *NT_create_sort_tree_by_other_tree_window(AW_root *aw_root, TREE_canvas *ntw) { |
---|
1078 | 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()); |
---|
1079 | |
---|
1080 | aws->callback(makeWindowCallback(sort_tree_by_other_tree_cb, ntw)); |
---|
1081 | aws->create_autosize_button("RESORT", "Sort according to source tree"); |
---|
1082 | |
---|
1083 | return aws; |
---|
1084 | } |
---|
1085 | |
---|
1086 | // --------------------------- |
---|
1087 | // multifurcate tree |
---|
1088 | |
---|
1089 | #define AWAR_MFURC "tree/multifurc/" |
---|
1090 | #define AWAR_MFURC_CONSIDER_BOOTSTRAP AWAR_MFURC "use_bs" |
---|
1091 | #define AWAR_MFURC_CONSIDER_LENGTH AWAR_MFURC "use_len" |
---|
1092 | #define AWAR_MFURC_CONSIDER_TERMINALS AWAR_MFURC "terminals" |
---|
1093 | #define AWAR_MFURC_LENGTH_LIMIT AWAR_MFURC "len" |
---|
1094 | #define AWAR_MFURC_BOOTSTRAP_LIMIT AWAR_MFURC "bs" |
---|
1095 | |
---|
1096 | void NT_create_multifurcate_tree_awars(AW_root *aw_root, AW_default props) { |
---|
1097 | aw_root->awar_int (AWAR_MFURC_CONSIDER_BOOTSTRAP, 0, props); |
---|
1098 | aw_root->awar_int (AWAR_MFURC_CONSIDER_LENGTH, 1, props); |
---|
1099 | aw_root->awar_int (AWAR_MFURC_CONSIDER_TERMINALS, 0, props); |
---|
1100 | aw_root->awar_float(AWAR_MFURC_LENGTH_LIMIT, 0.1, props); |
---|
1101 | aw_root->awar_float(AWAR_MFURC_BOOTSTRAP_LIMIT, 50, props); |
---|
1102 | } |
---|
1103 | static void multifurcation_cb(UNFIXED, TREE_canvas *ntw) { |
---|
1104 | AW_root *aw_root = ntw->aww->get_root(); |
---|
1105 | |
---|
1106 | float below_bootstrap = 101.0; |
---|
1107 | float below_length = 1000000.0; |
---|
1108 | bool applyAtLeafs = aw_root->awar(AWAR_MFURC_CONSIDER_TERMINALS)->read_int(); |
---|
1109 | |
---|
1110 | if (aw_root->awar(AWAR_MFURC_CONSIDER_BOOTSTRAP)->read_int()) below_bootstrap = aw_root->awar(AWAR_MFURC_BOOTSTRAP_LIMIT)->read_float(); |
---|
1111 | if (aw_root->awar(AWAR_MFURC_CONSIDER_LENGTH) ->read_int()) below_length = aw_root->awar(AWAR_MFURC_LENGTH_LIMIT) ->read_float(); |
---|
1112 | |
---|
1113 | NT_multifurcate_tree(ntw, TreeNode::multifurc_limits(below_bootstrap, below_length, applyAtLeafs)); |
---|
1114 | } |
---|
1115 | AW_window *NT_create_multifurcate_tree_window(AW_root *aw_root, TREE_canvas *ntw) { |
---|
1116 | AW_window_simple *aws = new AW_window_simple; |
---|
1117 | |
---|
1118 | aws->init(aw_root, ntw->aww->local_id("multifurcate"), "Multifurcate tree"); |
---|
1119 | aws->at(10, 10); |
---|
1120 | aws->auto_space(10, 10); |
---|
1121 | |
---|
1122 | aws->callback(AW_POPDOWN); |
---|
1123 | aws->create_button("CLOSE", "CLOSE", "C"); |
---|
1124 | |
---|
1125 | aws->callback(makeHelpCallback("multifurcate.hlp")); |
---|
1126 | aws->create_button("HELP", "HELP", "H"); |
---|
1127 | |
---|
1128 | const int LABEL_LENGTH = 46; |
---|
1129 | aws->label_length(LABEL_LENGTH); |
---|
1130 | |
---|
1131 | aws->at_newline(); |
---|
1132 | aws->label("Multifurcate branches with branchlength below"); |
---|
1133 | aws->create_toggle(AWAR_MFURC_CONSIDER_LENGTH); |
---|
1134 | aws->create_input_field(AWAR_MFURC_LENGTH_LIMIT, 10); |
---|
1135 | |
---|
1136 | aws->at_newline(); |
---|
1137 | aws->label(" AND bootstrap below"); |
---|
1138 | aws->create_toggle(AWAR_MFURC_CONSIDER_BOOTSTRAP); |
---|
1139 | aws->create_input_field(AWAR_MFURC_BOOTSTRAP_LIMIT, 10); |
---|
1140 | |
---|
1141 | aws->label_length(0); |
---|
1142 | aws->at_newline(); |
---|
1143 | aws->label("Also apply to terminal branches"); |
---|
1144 | aws->create_toggle(AWAR_MFURC_CONSIDER_TERMINALS); |
---|
1145 | |
---|
1146 | aws->at_newline(); |
---|
1147 | aws->callback(makeWindowCallback(multifurcation_cb, ntw)); |
---|
1148 | aws->create_autosize_button("MULTIFURCATE", "Multifurcate", "M"); |
---|
1149 | |
---|
1150 | return aws; |
---|
1151 | } |
---|
1152 | |
---|
1153 | // -------------------------------------------------------------------------------- |
---|
1154 | |
---|
1155 | #ifdef UNIT_TESTS |
---|
1156 | #ifndef TEST_UNIT_H |
---|
1157 | #include <test_unit.h> |
---|
1158 | #endif |
---|
1159 | |
---|
1160 | static const char *getTreeComment(GBDATA *gb_main, const char *treeName) { |
---|
1161 | GB_transaction ta(gb_main); |
---|
1162 | return GBT_tree_info_string(gb_main, treeName, -1); |
---|
1163 | } |
---|
1164 | |
---|
1165 | #define TEST_EXPECT_TREE_COMMENT_CONTAINS(treeName,expected) TEST_EXPECT_CONTAINS(getTreeComment(gb_main,treeName),expected) |
---|
1166 | #define TEST_EXPECT_TREE_COMMENT_DOESNT_CONTAIN(treeName,expected) TEST_EXPECT_DOESNT_CONTAIN(getTreeComment(gb_main,treeName),expected) |
---|
1167 | #define TEST_EXPECT_TREE_COMMENT_DOESNT_CONTAIN__BROKEN(treeName,expected) TEST_EXPECT_DOESNT_CONTAIN__BROKEN(getTreeComment(gb_main,treeName),expected) |
---|
1168 | |
---|
1169 | static GB_ERROR sort_namedtree_by_other_tree(GBDATA *gb_main, const char *tree, const char *other_tree) { |
---|
1170 | GB_ERROR error = NULp; |
---|
1171 | GB_transaction ta(gb_main); |
---|
1172 | SizeAwareTree *Tree = DOWNCAST(SizeAwareTree*, GBT_read_tree(gb_main, tree, new SizeAwareRoot)); |
---|
1173 | if (!Tree) error = GB_await_error(); |
---|
1174 | else { |
---|
1175 | Tree->compute_tree(); |
---|
1176 | error = sort_tree_by_other_tree(gb_main, Tree, other_tree); |
---|
1177 | if (!error) error = GBT_write_tree(gb_main, tree, Tree); |
---|
1178 | } |
---|
1179 | destroy(Tree); |
---|
1180 | return error; |
---|
1181 | } |
---|
1182 | |
---|
1183 | void TEST_sort_tree_by_other_tree() { |
---|
1184 | GB_shell shell; |
---|
1185 | GBDATA *gb_main = GB_open("TEST_trees.arb", "rw"); |
---|
1186 | TEST_REJECT_NULL(gb_main); |
---|
1187 | |
---|
1188 | 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);"; |
---|
1189 | 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);"; |
---|
1190 | 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);"; |
---|
1191 | |
---|
1192 | 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);"; |
---|
1193 | |
---|
1194 | TEST_EXPECT_DIFFERENT(topo_test, topo_center); |
---|
1195 | TEST_EXPECT_DIFFERENT(topo_test, topo_bottom); |
---|
1196 | TEST_EXPECT_DIFFERENT(topo_center, topo_bottom); |
---|
1197 | |
---|
1198 | // create sorted copies of tree_test |
---|
1199 | { |
---|
1200 | GB_transaction ta(gb_main); |
---|
1201 | SizeAwareTree *tree = DOWNCAST(SizeAwareTree*, GBT_read_tree(gb_main, "tree_test", new SizeAwareRoot)); |
---|
1202 | TEST_REJECT_NULL(tree); |
---|
1203 | TEST_EXPECT_NEWICK(nLENGTH, tree, topo_test); |
---|
1204 | |
---|
1205 | 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); |
---|
1206 | 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); |
---|
1207 | |
---|
1208 | // test SortByTopo |
---|
1209 | { |
---|
1210 | SortByTopo sbt(tree); |
---|
1211 | const double EPSILON = 0.0001; |
---|
1212 | |
---|
1213 | TEST_EXPECT_SIMILAR(sbt.sourcePos("CytAquat").value(), 0.0, EPSILON); // leftmost species (in topo_bottom) |
---|
1214 | TEST_EXPECT_SIMILAR(sbt.sourcePos("CloTyro4").value(), 1.0, EPSILON); // rightmost species |
---|
1215 | |
---|
1216 | TEST_EXPECT_SIMILAR(sbt.sourcePos("CurCitre").value(), 0.2857, EPSILON); // (5 of 15) |
---|
1217 | TEST_EXPECT_SIMILAR(sbt.sourcePos("CloButy2").value(), 0.5, EPSILON); // center species (8 of 15) |
---|
1218 | TEST_EXPECT_SIMILAR(sbt.sourcePos("CloTyrob").value(), 0.7857, EPSILON); // (12 of 15) |
---|
1219 | |
---|
1220 | TEST_REJECT(sbt.sourcePos("Un-Known").is_known()); // unknown species |
---|
1221 | } |
---|
1222 | |
---|
1223 | tree->reorder_tree(BIG_BRANCHES_TO_EDGE); TEST_EXPECT_NO_ERROR(GBT_write_tree(gb_main, "tree_work", tree)); |
---|
1224 | |
---|
1225 | destroy(tree); |
---|
1226 | } |
---|
1227 | |
---|
1228 | |
---|
1229 | 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); |
---|
1230 | 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); |
---|
1231 | 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); |
---|
1232 | 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); |
---|
1233 | |
---|
1234 | // TEST_EXPECT_NO_ERROR(GB_save_as(gb_main, "TEST_trees_save.arb", "b")); // test-save db to examine saved trees (do not commit!) |
---|
1235 | |
---|
1236 | // ---------------------------------------------------------------------------------------------------- |
---|
1237 | // test high-level function TREE_load_to_db (see #701; placed here by laziness, not related to sorting trees) |
---|
1238 | TEST_EXPECT_NO_ERROR(TREE_load_to_db(gb_main, "trees/test.tree", "tree_loaded")); // ../UNIT_TESTER/run/trees/test.tree |
---|
1239 | TEST_EXPECT_ERROR_CLEAR(); |
---|
1240 | 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);"); |
---|
1241 | TEST_EXPECT_TREE_COMMENT_CONTAINS("tree_loaded", "covering most of tree reader code"); // test comment |
---|
1242 | |
---|
1243 | GB_close(gb_main); |
---|
1244 | } |
---|
1245 | |
---|
1246 | void TEST_move_node_info() { |
---|
1247 | GB_shell shell; |
---|
1248 | GBDATA *gb_main = GB_open("TEST_trees.arb", "r"); |
---|
1249 | |
---|
1250 | const char *treeTarget1 = "tree_removal"; |
---|
1251 | const char *treeTarget2 = "tree_test"; |
---|
1252 | const char *treeSortby1 = "tree_removal_copy"; |
---|
1253 | const char *treeSortby2 = "tree_test_copy"; |
---|
1254 | |
---|
1255 | const char *treeSource1 = treeSortby2; // contains 1 group ("test") |
---|
1256 | const char *treeSource2 = "tree_tree2"; // contains 2 groups ("g2" + "outer") |
---|
1257 | const char *treeSource3 = "tree_groups"; // contains 5 groups |
---|
1258 | |
---|
1259 | #define GROUP_TEST "(CloTyrob,(CloTyro2,(CloTyro3,CloTyro4)))" |
---|
1260 | #define GROUP_TEST_FLIPPED "(((CloTyro3,CloTyro4),CloTyro2),CloTyrob)" |
---|
1261 | |
---|
1262 | #define NAMED_GROUP_TEST GROUP_TEST "'test'" |
---|
1263 | #define OVERWRITTEN_GROUP_TEST GROUP_TEST "'g2 [was: test]'" |
---|
1264 | |
---|
1265 | const char *org_topo1 = "((CloInnoc," GROUP_TEST "),(CloBifer,((CloCarni,CurCitre),((CloPaste,(Zombie1,(CloButy2,CloButyr))),(CytAquat,(CelBiazo,(CorGluta,(CorAquat,Zombie2))))))));"; |
---|
1266 | const char *org_topo2 = "((((" GROUP_TEST_FLIPPED ",CloInnoc),CloBifer),(((CloButy2,CloButyr),CloCarni),CloPaste)),((((CorAquat,CurCitre),CorGluta),CelBiazo),CytAquat));"; |
---|
1267 | |
---|
1268 | // (index convention := source target) |
---|
1269 | const char *unwanted_topo11 = "((CytAquat,(CelBiazo,(CorGluta,(CorAquat,Zombie2)))),((CloPaste,(Zombie1,(CloButy2,CloButyr))),((CloCarni,CurCitre),(CloBifer,(CloInnoc," NAMED_GROUP_TEST ")))));"; |
---|
1270 | const char *unwanted_topo21 = "((CloButy2,CloButyr),(Zombie1,(CloPaste,((((CloInnoc," OVERWRITTEN_GROUP_TEST "),CloBifer),(CloCarni,CurCitre)),(CytAquat,(CelBiazo,(CorGluta,(CorAquat,Zombie2)))))))'outer');"; |
---|
1271 | |
---|
1272 | const char *sorted_topo11 = "(((((CloInnoc," NAMED_GROUP_TEST "),CloBifer),(CloCarni,CurCitre)),(CloPaste,(Zombie1,(CloButy2,CloButyr)))),(CytAquat,(CelBiazo,(CorGluta,(CorAquat,Zombie2)))));"; |
---|
1273 | const char *sorted_topo21 = "(((((((CloInnoc," OVERWRITTEN_GROUP_TEST "),CloBifer),(CloCarni,CurCitre)),(CytAquat,(CelBiazo,(CorGluta,(CorAquat,Zombie2))))),CloPaste),Zombie1)'outer',(CloButy2,CloButyr));"; |
---|
1274 | const char *topo32 = "((CloButy2,CloButyr)'upper',(((((" GROUP_TEST_FLIPPED "'low2',CloInnoc),CloBifer),((((CorAquat,CurCitre),CorGluta),CelBiazo),CytAquat)'low1'),CloPaste),CloCarni));"; |
---|
1275 | const char *topo32_rc = "((CloButy2,CloButyr)'upper',(((((" GROUP_TEST_FLIPPED "'low2',CloInnoc),CloBifer),((((CorAquat,CurCitre),CorGluta),CelBiazo),CytAquat)'low1'),CloPaste),CloCarni)'lower');"; // @@@ should be same as topo32 (see #451) |
---|
1276 | const char *topo32_rel = "((CloButy2,CloButyr)" ",(((((" GROUP_TEST_FLIPPED "'low2 [p=0.250000;ir=100.0%;3->4]',CloInnoc),CloBifer)'low1 [p=0.232222;ir=100.0%;7->6]',((((CorAquat,CurCitre),CorGluta),CelBiazo),CytAquat)'upper [p=0.510000;ir=100.0%;5->5]'),CloPaste),CloCarni)'lower [p=0.230769;ir=100.0%;10->13]');"; // group 'upper' and 'low1' moved to different locations by relative scoring |
---|
1277 | const char *topo32_li = "((CloButy2,CloButyr)" ",(((((" GROUP_TEST_FLIPPED "'low2',CloInnoc),CloBifer),((((CorAquat,CurCitre),CorGluta),CelBiazo),CytAquat)'low1'),CloPaste),CloCarni)'lower');"; // group 'upper' filtered by limits |
---|
1278 | |
---|
1279 | 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));"; |
---|
1280 | |
---|
1281 | const char *LOG = "move_node_info.log"; |
---|
1282 | |
---|
1283 | // #define TEST_AUTO_UPDATE // uncomment to auto-update expected log-files |
---|
1284 | #if defined(TEST_AUTO_UPDATE) |
---|
1285 | # define TEST_LOGS_EXPECTED(expected) TEST_COPY_FILE(LOG, expected) |
---|
1286 | #else |
---|
1287 | # define TEST_LOGS_EXPECTED(expected) TEST_EXPECT_TEXTFILES_EQUAL(expected, LOG) |
---|
1288 | #endif // TEST_AUTO_UPDATE |
---|
1289 | |
---|
1290 | // create copies of 'tree_removal' + 'tree_test' |
---|
1291 | { |
---|
1292 | GB_transaction ta(gb_main); |
---|
1293 | |
---|
1294 | // remove existing comments from trees (already contains some log-entries tested below) |
---|
1295 | { |
---|
1296 | const char *resetComment = "<comment reset>"; |
---|
1297 | TEST_EXPECT_NO_ERROR(GBT_write_tree_remark(gb_main, treeTarget1, resetComment)); |
---|
1298 | TEST_EXPECT_NO_ERROR(GBT_write_tree_remark(gb_main, treeTarget2, resetComment)); |
---|
1299 | } |
---|
1300 | |
---|
1301 | TEST_EXPECT_NO_ERROR(GBT_copy_tree(gb_main, treeTarget1, treeSortby1)); |
---|
1302 | TEST_EXPECT_NO_ERROR(GBT_copy_tree(gb_main, treeTarget2, treeSortby2)); |
---|
1303 | |
---|
1304 | TEST_EXPECT_SAVED_NEWICK(nSIMPLE, gb_main, treeTarget1, org_topo1); |
---|
1305 | TEST_EXPECT_SAVED_NEWICK(nSIMPLE, gb_main, treeTarget2, org_topo2); |
---|
1306 | } |
---|
1307 | |
---|
1308 | GroupMatchScorer defaultScorer; |
---|
1309 | |
---|
1310 | // move node info |
---|
1311 | { |
---|
1312 | const char *comment_added = "Copied node info from tree_test_copy"; |
---|
1313 | TEST_EXPECT_TREE_COMMENT_DOESNT_CONTAIN(treeTarget1, comment_added); |
---|
1314 | |
---|
1315 | TEST_EXPECT_NO_ERROR(NTREE_move_tree_info(gb_main, treeSource1, treeTarget1, LOG, REMOVE_EXISTING_GROUPS, XFER_ALL_GROUPS, defaultScorer, NULp)); |
---|
1316 | TEST_LOGS_EXPECTED("group_xfer_11.log.expected"); |
---|
1317 | |
---|
1318 | TEST_EXPECT_SAVED_NEWICK__BROKEN(nSIMPLE, gb_main, treeTarget1, org_topo1); // @@@ moving node info modifies topology; caused by NT_tree_cmp.cxx@NAIVE_ROOTING |
---|
1319 | TEST_EXPECT_SAVED_NEWICK(nGROUP, gb_main, treeTarget1, unwanted_topo11); |
---|
1320 | TEST_EXPECT_TREE_COMMENT_CONTAINS(treeTarget1, comment_added); |
---|
1321 | |
---|
1322 | // @@@ when we have a function to set the root according to another tree (#449), |
---|
1323 | // use that function here. sorting tree after that, should again result in 'org_topo1'! |
---|
1324 | |
---|
1325 | TEST_EXPECT_NO_ERROR(sort_namedtree_by_other_tree(gb_main, treeTarget1, treeSortby1)); |
---|
1326 | TEST_EXPECT_SAVED_NEWICK(nGROUP, gb_main, treeTarget1, sorted_topo11); |
---|
1327 | } |
---|
1328 | { |
---|
1329 | const char *comment_added = "Copied node info from tree_groups"; |
---|
1330 | TEST_EXPECT_TREE_COMMENT_DOESNT_CONTAIN(treeTarget2, comment_added); |
---|
1331 | |
---|
1332 | TEST_EXPECT_NO_ERROR(NTREE_move_tree_info(gb_main, treeSource3, treeTarget2, LOG, REMOVE_EXISTING_GROUPS, XFER_ALL_GROUPS, defaultScorer, NULp)); |
---|
1333 | TEST_LOGS_EXPECTED("group_xfer_32.log.expected"); |
---|
1334 | |
---|
1335 | TEST_EXPECT_SAVED_NEWICK__BROKEN(nSIMPLE, gb_main, treeTarget2, org_topo2); // @@@ moving node info modifies topology; caused by NT_tree_cmp.cxx@NAIVE_ROOTING |
---|
1336 | TEST_EXPECT_TREE_COMMENT_CONTAINS(treeTarget2, comment_added); |
---|
1337 | TEST_EXPECT_SAVED_NEWICK(nGROUP, gb_main, treeTarget2, topo32); |
---|
1338 | |
---|
1339 | // perform same group-xfer after 1st xfer changed root => inserts 4 instead of 3 groups (obviously caused by changed root position; see #451) |
---|
1340 | TEST_EXPECT_NO_ERROR(NTREE_move_tree_info(gb_main, treeSource3, treeTarget2, LOG, REMOVE_EXISTING_GROUPS, XFER_ALL_GROUPS, defaultScorer, NULp)); |
---|
1341 | TEST_LOGS_EXPECTED("group_xfer_32_rc.log.expected"); |
---|
1342 | TEST_EXPECT_SAVED_NEWICK(nGROUP, gb_main, treeTarget2, topo32_rc); |
---|
1343 | |
---|
1344 | { |
---|
1345 | GroupMatchScorer relativeScorer; |
---|
1346 | relativeScorer.setPerErrorPenalties(0.0, 0.0, 0.0001); // remove absolute penalties for in-/outgroup |
---|
1347 | relativeScorer.setRelativePenalties(1.0, 1.0); // set relative penalties for in-/outgroup |
---|
1348 | |
---|
1349 | TEST_EXPECT_NO_ERROR(NTREE_move_tree_info(gb_main, treeSource3, treeTarget2, LOG, REMOVE_EXISTING_GROUPS, XFER_ALL_GROUPS, relativeScorer, |
---|
1350 | "groupname;\" [p=\";penalty;\";ir=\";ingroup;\";\";oldsize;\"->\";newsize;\"]\"")); |
---|
1351 | TEST_LOGS_EXPECTED("group_xfer_32_rel.log.expected"); |
---|
1352 | TEST_EXPECT_SAVED_NEWICK(nGROUP, gb_main, treeTarget2, topo32_rel); |
---|
1353 | } |
---|
1354 | |
---|
1355 | // again perform same group-xfer using ingroup- and outgroup-limit |
---|
1356 | { |
---|
1357 | GroupMatchScorer limitedScorer; |
---|
1358 | // limitedScorer.setLimits(RatioLimits(1.0, 1.0), RatioLimits(0.0, 0.0)); // filters all groups (upper, low2, low1 + lower) |
---|
1359 | // limitedScorer.setLimits(RatioLimits(0.0, 1.0), RatioLimits(0.0, 1.0)); // filters no group (by definition) |
---|
1360 | // limitedScorer.setLimits(RatioLimits(0.5, 1.0), RatioLimits(0.0, 1.0)); // group 'upper' placed at position with higher absolute penalty |
---|
1361 | // limitedScorer.setLimits(RatioLimits(0.75, 1.0), RatioLimits(0.0, 1.0)); // weird (does keel groups) |
---|
1362 | // limitedScorer.setLimits(RatioLimits(0.9, 1.0), RatioLimits(0.0, 1.0)); // removes group 'upper' |
---|
1363 | // limitedScorer.setLimits(RatioLimits(0.0, 1.0), RatioLimits(0.0, 0.5)); // filters no group |
---|
1364 | // limitedScorer.setLimits(RatioLimits(0.0, 1.0), RatioLimits(0.0, 0.1)); // filters groups 'twoleafs' + 'low2' (group 'lower' superseeded by 'low1') |
---|
1365 | // limitedScorer.setLimits(RatioLimits(0.0, 1.0), RatioLimits(0.0, 0.3)); // filters group 'twoleafs' |
---|
1366 | // limitedScorer.setLimits(RatioLimits(0.9, 1.0), RatioLimits(0.0, 0.1)); // filters all groups (weird) |
---|
1367 | limitedScorer.setLimits(RatioLimits(0.7, 1.0), RatioLimits(0.0, 0.3)); // filters group 'upper' + group 'twoleafs' |
---|
1368 | limitedScorer.setPerErrorPenalties(2.0, 2.0, 0.0002); // all values are 2*default -> result is same, scores are doubled! |
---|
1369 | |
---|
1370 | TEST_EXPECT_NO_ERROR(NTREE_move_tree_info(gb_main, treeSource3, treeTarget2, LOG, REMOVE_EXISTING_GROUPS, XFER_ALL_GROUPS, limitedScorer, "")); // test empty ACI does same as passing NULp |
---|
1371 | TEST_LOGS_EXPECTED("group_xfer_32_li.log.expected"); |
---|
1372 | TEST_EXPECT_SAVED_NEWICK(nGROUP, gb_main, treeTarget2, topo32_li); |
---|
1373 | } |
---|
1374 | } |
---|
1375 | |
---|
1376 | // add node info |
---|
1377 | { |
---|
1378 | const char *comment_added = "Added node info from tree_tree2"; |
---|
1379 | TEST_EXPECT_TREE_COMMENT_DOESNT_CONTAIN(treeTarget1, comment_added); |
---|
1380 | |
---|
1381 | TEST_EXPECT_NO_ERROR(NTREE_move_tree_info(gb_main, treeSource2, treeTarget1, LOG, KEEP_OLD_NAMES, XFER_ALL_GROUPS, defaultScorer, NULp)); |
---|
1382 | TEST_LOGS_EXPECTED("group_xfer_21.log.expected"); |
---|
1383 | |
---|
1384 | TEST_EXPECT_SAVED_NEWICK__BROKEN(nSIMPLE, gb_main, treeTarget1, org_topo1); // @@@ moving node info modifies topology; caused by NT_tree_cmp.cxx@NAIVE_ROOTING |
---|
1385 | TEST_EXPECT_SAVED_NEWICK(nGROUP, gb_main, treeTarget1, unwanted_topo21); |
---|
1386 | TEST_EXPECT_TREE_COMMENT_CONTAINS(treeTarget1, comment_added); |
---|
1387 | |
---|
1388 | // @@@ when we have a function to set the root according to another tree (#449), |
---|
1389 | // use that function here. sorting tree after that, should again result in 'org_topo1'! |
---|
1390 | |
---|
1391 | TEST_EXPECT_NO_ERROR(sort_namedtree_by_other_tree(gb_main, treeTarget1, treeSortby1)); |
---|
1392 | TEST_EXPECT_SAVED_NEWICK(nGROUP, gb_main, treeTarget1, sorted_topo21); |
---|
1393 | } |
---|
1394 | |
---|
1395 | // compare node info |
---|
1396 | { |
---|
1397 | const char *comment_added = "Compared topology with tree_test"; |
---|
1398 | TEST_EXPECT_TREE_COMMENT_DOESNT_CONTAIN(treeTarget1, comment_added); |
---|
1399 | |
---|
1400 | TEST_EXPECT_NO_ERROR(NTREE_move_tree_info(gb_main, treeSource1, treeTarget1, NULp, COMPARE_TOPOLOGY, XFER_ALL_GROUPS, defaultScorer, NULp)); |
---|
1401 | TEST_EXPECT_SAVED_NEWICK(nREMARK, gb_main, treeTarget1, compared_topo); |
---|
1402 | TEST_EXPECT_TREE_COMMENT_CONTAINS(treeTarget1, comment_added); |
---|
1403 | } |
---|
1404 | |
---|
1405 | // test error cases: |
---|
1406 | { |
---|
1407 | #define DOESNT_MATTER_ARGS gb_main,treeSource2,treeTarget1,LOG,REMOVE_EXISTING_GROUPS,XFER_ALL_GROUPS |
---|
1408 | |
---|
1409 | GroupMatchScorer invalidScoring; |
---|
1410 | |
---|
1411 | invalidScoring.setPerErrorPenalties(1.0, 0.0, 0.0001); |
---|
1412 | TEST_EXPECT_ERROR_CONTAINS(NTREE_move_tree_info(DOESNT_MATTER_ARGS, invalidScoring, NULp), "one outgroup penalty has to be different from zero"); |
---|
1413 | |
---|
1414 | invalidScoring.setPerErrorPenalties(0.0, 1.0, 0.0001); |
---|
1415 | TEST_EXPECT_ERROR_CONTAINS(NTREE_move_tree_info(DOESNT_MATTER_ARGS, invalidScoring, NULp), "one ingroup penalty has to be different from zero"); |
---|
1416 | |
---|
1417 | invalidScoring.setPerErrorPenalties(-1.0, 1.0, 0.0001); |
---|
1418 | TEST_EXPECT_ERROR_CONTAINS(NTREE_move_tree_info(DOESNT_MATTER_ARGS, invalidScoring, NULp), "invalid negative in/outgroup penalty"); |
---|
1419 | |
---|
1420 | invalidScoring.setPerErrorPenalties(1.0, 1.0, 0.0001); |
---|
1421 | invalidScoring.setRelativePenalties(100.0, -100.0); |
---|
1422 | TEST_EXPECT_ERROR_CONTAINS(NTREE_move_tree_info(DOESNT_MATTER_ARGS, invalidScoring, NULp), "invalid negative in/outgroup penalty"); |
---|
1423 | |
---|
1424 | #undef DOESNT_MATTER_ARGS |
---|
1425 | } |
---|
1426 | |
---|
1427 | GB_unlink(LOG); |
---|
1428 | GB_close(gb_main); |
---|
1429 | } |
---|
1430 | |
---|
1431 | __ATTR__REDUCED_OPTIMIZE void TEST_edges() { |
---|
1432 | GB_shell shell; |
---|
1433 | GBDATA *gb_main = GB_open("TEST_trees.arb", "rw"); |
---|
1434 | TEST_REJECT_NULL(gb_main); |
---|
1435 | |
---|
1436 | { |
---|
1437 | GB_transaction ta(gb_main); |
---|
1438 | TreeNode *tree = GBT_read_tree(gb_main, "tree_test", new SizeAwareRoot); |
---|
1439 | |
---|
1440 | TreeNode *left = tree->findLeafNamed("CloTyro3"); TEST_REJECT_NULL(left); |
---|
1441 | TreeNode *node = left->get_father(); TEST_REJECT_NULL(node); |
---|
1442 | TreeNode *right = node->findLeafNamed("CloTyro4"); TEST_REJECT_NULL(right); |
---|
1443 | |
---|
1444 | TEST_EXPECT(node == right->get_father()); |
---|
1445 | TEST_EXPECT(node->get_leftson() == left); |
---|
1446 | TEST_EXPECT(node->get_rightson() == right); |
---|
1447 | |
---|
1448 | TreeNode *parent = node->get_father(); TEST_REJECT_NULL(parent); |
---|
1449 | TreeNode *brother = parent->findLeafNamed("CloTyro2"); TEST_REJECT_NULL(brother); |
---|
1450 | |
---|
1451 | TEST_EXPECT(node->get_brother() == brother); |
---|
1452 | |
---|
1453 | TreeNode *grandpa = parent->get_father(); TEST_REJECT_NULL(grandpa); |
---|
1454 | |
---|
1455 | // topology: |
---|
1456 | // |
---|
1457 | // grandpa |
---|
1458 | // / |
---|
1459 | // / |
---|
1460 | // / |
---|
1461 | // parent |
---|
1462 | // /\ . |
---|
1463 | // / \ . |
---|
1464 | // / \ . |
---|
1465 | // node brother |
---|
1466 | // /\ . |
---|
1467 | // / \ . |
---|
1468 | // / \ . |
---|
1469 | // left right |
---|
1470 | |
---|
1471 | // test next() and otherNext() for inner edge 'node->parent' |
---|
1472 | { |
---|
1473 | ARB_edge nodeUp = parentEdge(node); |
---|
1474 | |
---|
1475 | TEST_EXPECT(node->is_leftson()); // if child is left son.. |
---|
1476 | TEST_EXPECT(nodeUp.next().dest() == grandpa); // .. next() continues rootwards |
---|
1477 | TEST_EXPECT(nodeUp.counter_next().dest() == brother); |
---|
1478 | |
---|
1479 | ARB_edge brotherUp = parentEdge(brother); |
---|
1480 | |
---|
1481 | TEST_EXPECT(brother->is_rightson()); // if child is right son.. |
---|
1482 | TEST_EXPECT(brotherUp.next().dest() == node); // .. next() continues with other son |
---|
1483 | TEST_EXPECT(brotherUp.counter_next().dest() == grandpa); |
---|
1484 | |
---|
1485 | ARB_edge down = nodeUp.inverse(); |
---|
1486 | |
---|
1487 | TEST_EXPECT(down.next().dest() == right); // next descends into right son |
---|
1488 | TEST_EXPECT(down.counter_next().dest() == left); |
---|
1489 | |
---|
1490 | TEST_EXPECT(nodeUp.previous().source() == left); |
---|
1491 | TEST_EXPECT(nodeUp.counter_previous().source() == right); |
---|
1492 | |
---|
1493 | ARB_edge toLeaf(node, left); |
---|
1494 | TEST_EXPECT(toLeaf.is_edge_to_leaf()); |
---|
1495 | |
---|
1496 | // all iterators should turn around at leaf: |
---|
1497 | TEST_EXPECT(toLeaf.next().dest() == node); |
---|
1498 | TEST_EXPECT(toLeaf.counter_next().dest() == node); |
---|
1499 | |
---|
1500 | ARB_edge fromLeaf(left, node); |
---|
1501 | TEST_EXPECT(fromLeaf.previous().dest() == left); |
---|
1502 | TEST_EXPECT(fromLeaf.counter_previous().dest() == left); |
---|
1503 | |
---|
1504 | ARB_edge rootEdge(tree->get_leftson(), tree->get_rightson()); |
---|
1505 | |
---|
1506 | TEST_EXPECT(rootEdge.get_type() == ROOT_EDGE); |
---|
1507 | TEST_EXPECT(nodeUp.get_type() == EDGE_TO_ROOT); |
---|
1508 | TEST_EXPECT(fromLeaf.get_type() == EDGE_TO_ROOT); |
---|
1509 | TEST_EXPECT(down.get_type() == EDGE_TO_LEAF); |
---|
1510 | TEST_EXPECT(toLeaf.get_type() == EDGE_TO_LEAF); |
---|
1511 | |
---|
1512 | // test iterators are inverse functions |
---|
1513 | { |
---|
1514 | ARB_edge e[] = { nodeUp, down, toLeaf, fromLeaf, rootEdge }; |
---|
1515 | const int EDGES = ARRAY_ELEMS(e); |
---|
1516 | for (int i = 0; i<EDGES; ++i) { |
---|
1517 | TEST_ANNOTATE(GBS_global_string("i=%i", i)); |
---|
1518 | |
---|
1519 | TEST_EXPECT(e[i].next().previous() == e[i]); |
---|
1520 | TEST_EXPECT(e[i].previous().next() == e[i]); |
---|
1521 | |
---|
1522 | TEST_EXPECT(e[i].counter_next().counter_previous() == e[i]); |
---|
1523 | TEST_EXPECT(e[i].counter_previous().counter_next() == e[i]); |
---|
1524 | |
---|
1525 | ARB_edge inv(e[i].inverse()); |
---|
1526 | |
---|
1527 | TEST_EXPECT(e[i].counter_next().inverse().next() == inv); |
---|
1528 | TEST_EXPECT(e[i].counter_previous().inverse().previous() == inv); |
---|
1529 | TEST_EXPECT(e[i].next().inverse().counter_next() == inv); |
---|
1530 | TEST_EXPECT(e[i].previous().inverse().counter_previous() == inv); |
---|
1531 | } |
---|
1532 | } |
---|
1533 | |
---|
1534 | // test adjacent_distance |
---|
1535 | const double EPSILON = 0.000001; |
---|
1536 | |
---|
1537 | const double NLEN = 0.025806; |
---|
1538 | const double BLEN = 0.017316; |
---|
1539 | const double PLEN = 0.017167; |
---|
1540 | const double LLEN = 1.045690; |
---|
1541 | const double RLEN = 0.060606; |
---|
1542 | |
---|
1543 | TEST_EXPECT_SIMILAR(node->get_branchlength(), NLEN, EPSILON); |
---|
1544 | TEST_EXPECT_SIMILAR(nodeUp.length(), NLEN, EPSILON); |
---|
1545 | TEST_EXPECT_SIMILAR(down.length(), NLEN, EPSILON); |
---|
1546 | TEST_EXPECT_SIMILAR(nodeUp.length_or_adjacent_distance(), NLEN, EPSILON); |
---|
1547 | TEST_EXPECT_SIMILAR(down.length_or_adjacent_distance(), NLEN, EPSILON); |
---|
1548 | |
---|
1549 | TEST_EXPECT_SIMILAR(brother->get_branchlength(), BLEN, EPSILON); |
---|
1550 | TEST_EXPECT_SIMILAR(parent ->get_branchlength(), PLEN, EPSILON); |
---|
1551 | TEST_EXPECT_SIMILAR(nodeUp.adjacent_distance(), BLEN+PLEN, EPSILON); |
---|
1552 | |
---|
1553 | TEST_EXPECT_SIMILAR(left ->get_branchlength(), LLEN, EPSILON); |
---|
1554 | TEST_EXPECT_SIMILAR(right->get_branchlength(), RLEN, EPSILON); |
---|
1555 | TEST_EXPECT_SIMILAR(down.adjacent_distance(), LLEN+RLEN, EPSILON); |
---|
1556 | |
---|
1557 | // modify lengths |
---|
1558 | const double MOD_NLEN = 0.123456; |
---|
1559 | const double MOD_LLEN = 0.246802; |
---|
1560 | |
---|
1561 | toLeaf.set_length(MOD_LLEN); |
---|
1562 | nodeUp.set_length(MOD_NLEN); |
---|
1563 | |
---|
1564 | TEST_EXPECT_SIMILAR(toLeaf.length(), MOD_LLEN, EPSILON); |
---|
1565 | TEST_EXPECT_SIMILAR(nodeUp.length(), MOD_NLEN, EPSILON); |
---|
1566 | TEST_EXPECT_SIMILAR(down.length(), MOD_NLEN, EPSILON); |
---|
1567 | } |
---|
1568 | |
---|
1569 | destroy(tree); |
---|
1570 | } |
---|
1571 | |
---|
1572 | GB_close(gb_main); |
---|
1573 | } |
---|
1574 | |
---|
1575 | void TEST_remove_bootstraps() { |
---|
1576 | GB_shell shell; |
---|
1577 | GBDATA *gb_main = GB_open("TEST_trees.arb", "rw"); |
---|
1578 | TEST_REJECT_NULL(gb_main); |
---|
1579 | |
---|
1580 | { |
---|
1581 | GB_transaction ta(gb_main); |
---|
1582 | TreeNode *tree = GBT_read_tree(gb_main, "tree_test", new SizeAwareRoot); |
---|
1583 | TEST_REJECT_NULL(tree); |
---|
1584 | |
---|
1585 | const char *topo_org = "(((((((CloTyro3,CloTyro4)'40%',CloTyro2)'0%',CloTyrob)'97%',CloInnoc)'0%',CloBifer)'53%',(((CloButy2,CloButyr),CloCarni)'33%',CloPaste)'97%'),((((CorAquat,CurCitre),CorGluta)'17%',CelBiazo)'40%',CytAquat));"; |
---|
1586 | const char *topo_rem = "(((((((CloTyro3,CloTyro4)" ",CloTyro2)" ",CloTyrob)" ",CloInnoc)" ",CloBifer)" ",(((CloButy2,CloButyr),CloCarni)" ",CloPaste)" "),((((CorAquat,CurCitre),CorGluta)" ",CelBiazo)" ",CytAquat));"; |
---|
1587 | |
---|
1588 | TEST_EXPECT_NEWICK(nREMARK, tree, topo_org); |
---|
1589 | |
---|
1590 | tree->remove_bootstrap(); |
---|
1591 | TEST_EXPECT_NEWICK(nREMARK, tree, topo_rem); |
---|
1592 | |
---|
1593 | destroy(tree); |
---|
1594 | } |
---|
1595 | |
---|
1596 | GB_close(gb_main); |
---|
1597 | } |
---|
1598 | |
---|
1599 | void TEST_multifurcate_tree() { |
---|
1600 | GB_shell shell; |
---|
1601 | GBDATA *gb_main = GB_open("TEST_trees.arb", "rw"); |
---|
1602 | TEST_REJECT_NULL(gb_main); |
---|
1603 | |
---|
1604 | 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):0.564,CloCarni:0.120)'33%':0.010,CloPaste:0.179)'97%':0.131):0.081,((((CorAquat:0.084,CurCitre:0.058):0.103,CorGluta:0.522)'17%':0.053,CelBiazo:0.059)'40%':0.207,CytAquat:0.711):0.081);"; |
---|
1605 | // changes = " +0.307 -0.371 +0.064 " |
---|
1606 | 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):0.564,CloCarni:0.120)'33%':0.010,CloPaste:0.179)'97%':0.131):0.081,((((CorAquat:0.084,CurCitre:0.058):0.103,CorGluta:0.522)'17%':0.053,CelBiazo:0.059)'40%':0.207,CytAquat:0.711):0.081);"; |
---|
1607 | 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):0.578,CloCarni:0.121)" ":0.000,CloPaste:0.181)'97%':0.132):0.081,((((CorAquat:0.084,CurCitre:0.058):0.103,CorGluta:0.522)'17%':0.053,CelBiazo:0.059)'40%':0.207,CytAquat:0.711):0.081);"; |
---|
1608 | 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):0.570,CloCarni:0.121)" ":0.000,CloPaste:0.181)'97%':0.132):0.081,((((CorAquat:0.084,CurCitre:0.058):0.103,CorGluta:0.522)'17%':0.053,CelBiazo:0.059)'40%':0.207,CytAquat:0.711):0.081);"; |
---|
1609 | 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):0.564,CloCarni:0.120)'33%':0.010,CloPaste:0.179)'97%':0.131):0.081,((((CorAquat:0.084,CurCitre:0.058):0.103,CorGluta:0.522)'17%':0.053,CelBiazo:0.059)'40%':0.207,CytAquat:0.711):0.081);"; |
---|
1610 | 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):0.564,CloCarni:0.120)'33%':0.010,CloPaste:0.179)'97%':0.131):0.081,((((CorAquat:0.084,CurCitre:0.058):0.109,CorGluta:0.554)" ":0.000,CelBiazo:0.062)'40%':0.220,CytAquat:0.711):0.081);"; |
---|
1611 | 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);"; |
---|
1612 | |
---|
1613 | const double STABLE_LENGTH = 5.362750; |
---|
1614 | const double EPSILON = 0.000001; |
---|
1615 | |
---|
1616 | for (int test = 1; test<=6; ++test) { |
---|
1617 | GB_transaction ta(gb_main); |
---|
1618 | TreeNode *tree = GBT_read_tree(gb_main, "tree_test", new SizeAwareRoot); |
---|
1619 | |
---|
1620 | TEST_REJECT_NULL(tree); |
---|
1621 | if (test == 1) { |
---|
1622 | TEST_EXPECT_NEWICK(nALL, tree, topo_test); |
---|
1623 | TEST_EXPECT_SIMILAR(tree->sum_child_lengths(), STABLE_LENGTH, EPSILON); |
---|
1624 | } |
---|
1625 | |
---|
1626 | switch (test) { |
---|
1627 | case 1: |
---|
1628 | tree->multifurcate_whole_tree(TreeNode::multifurc_limits(101, 0.05, true)); |
---|
1629 | TEST_EXPECT_NEWICK(nALL, tree, topo_bs_less_101_005); |
---|
1630 | TEST_EXPECT_SIMILAR(tree->sum_child_lengths(), STABLE_LENGTH, EPSILON); |
---|
1631 | break; |
---|
1632 | case 6: |
---|
1633 | tree->multifurcate_whole_tree(TreeNode::multifurc_limits(101, 0.05, false)); |
---|
1634 | TEST_EXPECT_NEWICK(nALL, tree, topo_bs_less_101_005_NT); |
---|
1635 | TEST_EXPECT_SIMILAR(tree->sum_child_lengths(), STABLE_LENGTH, EPSILON); |
---|
1636 | break; |
---|
1637 | case 2: |
---|
1638 | tree->multifurcate_whole_tree(TreeNode::multifurc_limits(30, 0.05, true)); |
---|
1639 | TEST_EXPECT_NEWICK(nALL, tree, topo_bs_less_30_005); |
---|
1640 | TEST_EXPECT_SIMILAR(tree->sum_child_lengths(), STABLE_LENGTH, EPSILON); |
---|
1641 | break; |
---|
1642 | case 3: |
---|
1643 | tree->multifurcate_whole_tree(TreeNode::multifurc_limits(30, 1000, true)); |
---|
1644 | TEST_EXPECT_NEWICK(nALL, tree, topo_bs_less_30); |
---|
1645 | TEST_EXPECT_SIMILAR(tree->sum_child_lengths(), STABLE_LENGTH, EPSILON); |
---|
1646 | break; |
---|
1647 | case 4: |
---|
1648 | tree->multifurcate_whole_tree(TreeNode::multifurc_limits(101, 1000, true)); // multifurcate all |
---|
1649 | TEST_EXPECT_NEWICK(nALL, tree, topo_all); |
---|
1650 | TEST_EXPECT_SIMILAR(tree->sum_child_lengths(), 0.0, EPSILON); |
---|
1651 | break; |
---|
1652 | case 5: { |
---|
1653 | TreeNode *CloInnoc = tree->findLeafNamed("CloInnoc"); |
---|
1654 | TEST_REJECT_NULL(CloInnoc); |
---|
1655 | |
---|
1656 | parentEdge(CloInnoc).multifurcate(); |
---|
1657 | TEST_EXPECT_NEWICK(nALL, tree, topo_single); |
---|
1658 | |
---|
1659 | TEST_EXPECT_SIMILAR(tree->sum_child_lengths(), STABLE_LENGTH, EPSILON); |
---|
1660 | break; |
---|
1661 | } |
---|
1662 | default: |
---|
1663 | nt_assert(0); |
---|
1664 | break; |
---|
1665 | } |
---|
1666 | |
---|
1667 | destroy(tree); |
---|
1668 | } |
---|
1669 | |
---|
1670 | GB_close(gb_main); |
---|
1671 | } |
---|
1672 | |
---|
1673 | void TEST_TreeNode_attributes() { |
---|
1674 | // -> ../UNIT_TESTER/run/trees/bg_exp_p__0.tree |
---|
1675 | TreeNode *tree = TREE_load("trees/bg_exp_p__0.tree", new SimpleRoot, NULp, false, NULp); |
---|
1676 | |
---|
1677 | TreeNode *MabPelag = tree->findLeafNamed("MabPelag"); |
---|
1678 | TreeNode *MabSalin = tree->findLeafNamed("MabSalin"); |
---|
1679 | TreeNode *PaoMaris = tree->findLeafNamed("PaoMaris"); |
---|
1680 | |
---|
1681 | TEST_EXPECT(MabPelag->father == MabSalin->father); // are brothers |
---|
1682 | |
---|
1683 | TreeNode *Mabs = MabPelag->father; |
---|
1684 | TreeNode *PaoMabs = PaoMaris->father; |
---|
1685 | |
---|
1686 | // ((MabPelag, MabSalin), PaoMaris) |
---|
1687 | // -------- Mabs ------ |
---|
1688 | // -------------- PaoMabs --------- |
---|
1689 | |
---|
1690 | TEST_EXPECT(Mabs->father == PaoMabs); |
---|
1691 | |
---|
1692 | // is_son_of |
---|
1693 | TEST_EXPECT(MabPelag->is_son_of(Mabs)); |
---|
1694 | TEST_EXPECT(MabSalin->is_son_of(Mabs)); |
---|
1695 | TEST_EXPECT(Mabs->is_son_of(PaoMabs)); |
---|
1696 | TEST_EXPECT(PaoMaris->is_son_of(PaoMabs)); |
---|
1697 | |
---|
1698 | // is_inside |
---|
1699 | TEST_EXPECT(MabPelag->is_inside(Mabs)); // leaf in father |
---|
1700 | TEST_EXPECT(Mabs->is_inside(PaoMabs)); // node in father |
---|
1701 | TEST_EXPECT(MabPelag->is_inside(PaoMabs)); // leaf in grandfather |
---|
1702 | TEST_EXPECT(MabPelag->is_inside(MabPelag)); // self-containment |
---|
1703 | TEST_REJECT(Mabs->is_inside(MabPelag)); // not: father in child |
---|
1704 | TEST_REJECT(MabPelag->is_inside(MabSalin)); // not: node in brother |
---|
1705 | |
---|
1706 | // is_ancestor_of |
---|
1707 | TEST_EXPECT(Mabs->is_ancestor_of(MabPelag)); |
---|
1708 | TEST_EXPECT(Mabs->is_ancestor_of(MabSalin)); |
---|
1709 | TEST_REJECT(Mabs->is_ancestor_of(PaoMaris)); // brother is no ancestor |
---|
1710 | TEST_REJECT(Mabs->is_ancestor_of(Mabs)); // node never is ancestor of itself |
---|
1711 | TEST_REJECT(Mabs->is_ancestor_of(PaoMabs)); // child cannot be ancestor |
---|
1712 | |
---|
1713 | TEST_EXPECT(PaoMabs->is_ancestor_of(MabPelag)); // root of subtree (PaoMabs) is ancestor of all members.. |
---|
1714 | TEST_EXPECT(PaoMabs->is_ancestor_of(MabSalin)); |
---|
1715 | TEST_EXPECT(PaoMabs->is_ancestor_of(PaoMaris)); |
---|
1716 | TEST_EXPECT(PaoMabs->is_ancestor_of(Mabs)); |
---|
1717 | TEST_REJECT(PaoMabs->is_ancestor_of(PaoMabs)); // .. despite itself |
---|
1718 | |
---|
1719 | // in_same_branch_as / in_other_branch_than |
---|
1720 | TEST_EXPECT(MabPelag->in_same_branch_as(MabPelag)); |
---|
1721 | TEST_EXPECT(MabPelag->in_other_branch_than(MabSalin)); |
---|
1722 | TEST_EXPECT(MabPelag->in_other_branch_than(PaoMaris)); |
---|
1723 | TEST_EXPECT(MabPelag->in_same_branch_as(Mabs)); |
---|
1724 | TEST_EXPECT(MabPelag->in_same_branch_as(PaoMabs)); |
---|
1725 | |
---|
1726 | TEST_EXPECT(PaoMabs->in_same_branch_as(MabPelag)); |
---|
1727 | TEST_EXPECT(PaoMabs->in_same_branch_as(MabSalin)); |
---|
1728 | TEST_EXPECT(PaoMabs->in_same_branch_as(PaoMaris)); |
---|
1729 | TEST_EXPECT(PaoMabs->in_same_branch_as(Mabs)); |
---|
1730 | TEST_EXPECT(PaoMabs->in_same_branch_as(PaoMabs)); |
---|
1731 | |
---|
1732 | TEST_EXPECT(Mabs->in_same_branch_as(MabPelag)); |
---|
1733 | TEST_EXPECT(Mabs->in_same_branch_as(MabSalin)); |
---|
1734 | TEST_EXPECT(Mabs->in_other_branch_than(PaoMaris)); |
---|
1735 | TEST_EXPECT(Mabs->in_same_branch_as(Mabs)); |
---|
1736 | TEST_EXPECT(Mabs->in_same_branch_as(PaoMabs)); |
---|
1737 | |
---|
1738 | // ancestor_common_with |
---|
1739 | TEST_EXPECT(MabPelag->ancestor_common_with(MabSalin) == Mabs); |
---|
1740 | TEST_EXPECT(MabSalin->ancestor_common_with(MabPelag) == Mabs); |
---|
1741 | TEST_EXPECT(PaoMaris->ancestor_common_with(MabPelag) == PaoMabs); |
---|
1742 | TEST_EXPECT(PaoMabs->ancestor_common_with(Mabs) == PaoMabs); |
---|
1743 | TEST_EXPECT(MabPelag->ancestor_common_with(PaoMabs) == PaoMabs); |
---|
1744 | |
---|
1745 | destroy(tree); |
---|
1746 | } |
---|
1747 | |
---|
1748 | #endif // UNIT_TESTS |
---|
1749 | |
---|
1750 | // -------------------------------------------------------------------------------- |
---|
1751 | |
---|