~ubuntu-branches/debian/jessie/arb/jessie

« back to all changes in this revision

Viewing changes to TOOLS/arb_consensus_tree.cxx

  • Committer: Package Import Robot
  • Author(s): Elmar Pruesse, Andreas Tille, Elmar Pruesse
  • Date: 2014-09-02 15:15:06 UTC
  • mfrom: (1.1.6)
  • Revision ID: package-import@ubuntu.com-20140902151506-jihq58b3iz342wif
Tags: 6.0.2-1
[ Andreas Tille ]
* New upstream version
  Closes: #741890
* debian/upstream -> debian/upstream/metadata
* debian/control:
   - Build-Depends: added libglib2.0-dev
   - Depends: added mafft, mrbayes
* debian/rules
   - Add explicite --remove-section=.comment option to manual strip call
* cme fix dpkg-control
* arb-common.dirs: Do not create unneeded lintian dir
* Add turkish debconf translation (thanks for the patch to Mert Dirik
  <mertdirik@gmail.com>)
  Closes: #757497

[ Elmar Pruesse ]
* patches removed:
   - 10_config.makefiles.patch,
     80_no_GL.patch
       removed in favor of creating file from config.makefile.template via 
       sed in debian/control
   - 20_Makefile_main.patch
       merged upstream
   - 21_Makefiles.patch
       no longer needed
   - 30_tmpfile_CVE-2008-5378.patch: 
       merged upstream
   - 50_fix_gcc-4.8.patch:
       merged upstream
   - 40_add_libGLU.patch:
       libGLU not needed for arb_ntree)
   - 60_use_debian_packaged_raxml.patch:
       merged upstream
   - 70_hardening.patch
       merged upstream
   - 72_add_math_lib_to_linker.patch
       does not appear to be needed
* patches added:
   - 10_upstream_r12793__show_db_load_progress:
       backported patch showing progress while ARB is loading a database
       (needed as indicator/splash screen while ARB is launching)
   - 20_upstream_r12794__socket_permissions:
       backported security fix
   - 30_upstream_r12814__desktop_keywords:
       backported add keywords to desktop (fixes lintian warning)
   - 40_upstream_r12815__lintian_spelling:
       backported fix for lintian reported spelling errors
   - 50_private_nameservers
       change configuration to put nameservers into users home dirs
       (avoids need for shared writeable directory)
   - 60_use_debian_phyml
       use phyml from debian package for both interfaces in ARB
* debian/rules:
   - create config.makefile from override_dh_configure target
   - use "make tarfile" in override_dh_install
   - remove extra cleaning not needed for ARB 6
   - use "dh_install --list-missing" to avoid missing files
   - added override_dh_fixperms target
* debian/control:
   - added libarb-dev package
   - Depends: added phyml, xdg-utils
   - Suggests: removed phyml
   - fix lintian duplicate-short-description (new descriptions)
* debian/*.install:
   - "unrolled" confusing globbing to select files
   - pick files from debian/tmp
   - moved all config files to /etc/arb
* debian/arb-common.templates: updated
* scripts:
   - removed arb-add-pt-server
   - launch-wrapper: 
     - only add demo.arb to newly created $ARBUSERDATA
     - pass commandline arguments through bin/arb wrapper
   - preinst: removing old PT server index files on upgrade from 5.5*
   - postinst: set setgid on shared PT dir
* rewrote arb.1 manfile
* added file icon for ARB databases
* using upstream arb_tcp.dat

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// ============================================================= //
 
2
//                                                               //
 
3
//   File      : arb_consensus_tree.cxx                          //
 
4
//   Purpose   : build consensus tree with the same library      //
 
5
//               as ARB-NJ                                       //
 
6
//                                                               //
 
7
//   Coded by Ralf Westram (coder@reallysoft.de) in March 2012   //
 
8
//   Institute of Microbiology (Technical University Munich)     //
 
9
//   http://www.arb-home.de/                                     //
 
10
//                                                               //
 
11
// ============================================================= //
 
12
 
 
13
#include <CT_ctree.hxx>
 
14
#include <TreeRead.h>
 
15
#include <TreeWrite.h>
 
16
#include <arb_str.h>
 
17
#include <arb_diff.h>
 
18
#include <arb_defs.h>
 
19
 
 
20
using namespace std;
 
21
 
 
22
static GBT_TREE *build_consensus_tree(const CharPtrArray& input_trees, GB_ERROR& error, size_t& different_species, double weight, char *&comment) {
 
23
    // read all input trees, generate and return consensus tree
 
24
    // (Note: the 'weight' used here doesn't matter atm, since all trees are added with the same weight)
 
25
 
 
26
    arb_assert(!error);
 
27
    error   = NULL;
 
28
    comment = NULL;
 
29
 
 
30
    GBT_TREE *consense_tree = NULL;
 
31
    if (input_trees.empty()) {
 
32
        error = "no trees given";
 
33
    }
 
34
    else {
 
35
        ConsensusTreeBuilder tree_builder;
 
36
 
 
37
        for (size_t i = 0; !error && i<input_trees.size(); ++i) {
 
38
            char *warnings = NULL;
 
39
 
 
40
            TreeRoot      *root = new TreeRoot(new SizeAwareNodeFactory, true); // will be deleted when tree gets deleted
 
41
            SizeAwareTree *tree = DOWNCAST(SizeAwareTree*, TREE_load(input_trees[i], *root, NULL, true, &warnings));
 
42
            if (!tree) {
 
43
                error = GBS_global_string("Failed to load tree '%s' (Reason: %s)", input_trees[i], GB_await_error());
 
44
            }
 
45
            else {
 
46
                if (warnings) {
 
47
                    GB_warningf("while loading tree '%s':\n%s", input_trees[i], warnings);
 
48
                    free(warnings);
 
49
                }
 
50
                tree_builder.add(tree, input_trees[i], weight);
 
51
            }
 
52
        }
 
53
 
 
54
        if (!error) consense_tree = tree_builder.get(different_species, error);
 
55
        if (!error) comment = tree_builder.get_remark();
 
56
    }
 
57
    arb_assert(contradicted(consense_tree, error));
 
58
    return consense_tree;
 
59
}
 
60
 
 
61
static char *create_tree_name(const char *savename) {
 
62
    // create a DB treename (using savename as hint)
 
63
    char *tree_name;
 
64
    {
 
65
        // as default use part behind '/' and remove file extension
 
66
        const char *lslash   = strrchr(savename, '/');
 
67
        if (lslash) savename = lslash+1;
 
68
 
 
69
        const char *ldot = strrchr(savename, '.');
 
70
 
 
71
        tree_name = ldot ? GB_strpartdup(savename, ldot-1) : strdup(savename);
 
72
        if (tree_name[0] == 0) freedup(tree_name, "tree_consensus");
 
73
    }
 
74
 
 
75
    // make sure tree name starts with 'tree_'
 
76
    if (!ARB_strBeginsWith(tree_name, "tree_")) {
 
77
        freeset(tree_name, GBS_global_string_copy("tree_%s", tree_name));
 
78
    }
 
79
    return tree_name;
 
80
}
 
81
 
 
82
static GB_ERROR save_tree_as_newick(GBT_TREE *tree, const char *savename, const char *comment) {
 
83
    // save a tree to a newick file
 
84
 
 
85
    // since ARB only saves trees out of a database,
 
86
    // i use a hack here:
 
87
    // - create temp DB
 
88
    // - save tree there
 
89
    // - save to newick as usual
 
90
 
 
91
    GB_shell  shell;
 
92
    GBDATA   *gb_main = GB_open("", "crw");
 
93
    GB_ERROR  error   = NULL;
 
94
 
 
95
    if (!gb_main) {
 
96
        error = GB_await_error();
 
97
    }
 
98
    else {
 
99
        char *db_tree_name = create_tree_name(savename);
 
100
 
 
101
        {
 
102
            GB_transaction ta(gb_main);
 
103
            error = GBT_write_tree_with_remark(gb_main, db_tree_name, tree, comment);
 
104
        }
 
105
        if (!error) error = TREE_write_Newick(gb_main, db_tree_name, NULL, true, true, true, true, TREE_SINGLE_QUOTES, savename);
 
106
 
 
107
        free(db_tree_name);
 
108
        GB_close(gb_main);
 
109
    }
 
110
 
 
111
    if (error) {
 
112
        error = GBS_global_string("Failed to save tree to '%s' (Reason: %s)", savename, error);
 
113
    }
 
114
 
 
115
    return error;
 
116
}
 
117
 
 
118
int ARB_main(int argc, char *argv[]) {
 
119
    GB_ERROR error = NULL;
 
120
 
 
121
    if (argc<2) {
 
122
        printf("Usage: arb_consensus_tree [options] [tree]+\n"
 
123
               "Purpose: Create a consensus tree out of multiple trees\n"
 
124
               "  options:\n"
 
125
               "  -w outfile      write consensus tree to outfile\n");
 
126
 
 
127
        // @@@ wanted options
 
128
        // - do not add relative frequency of used subtrees as bootstrap values
 
129
        // - multifurcate branches with bootstrap value below XXX
 
130
        // - eliminate branches with bootstrap value below YYY
 
131
        // - ... ?
 
132
    }
 
133
    else {
 
134
        char *savename = NULL;
 
135
 
 
136
        ConstStrArray input_tree_names;
 
137
 
 
138
        for (int a = 1; a<argc; ++a) {
 
139
            const char *arg = argv[a];
 
140
            if (arg[0] == '-') {
 
141
                switch (arg[1]) {
 
142
                    case 'w': savename = strdup(argv[++a]); break;
 
143
                    default : error = GBS_global_string("Unknown switch '-%c'", arg[1]); break;
 
144
                }
 
145
            }
 
146
            else {
 
147
                input_tree_names.put(argv[a]);
 
148
            }
 
149
        }
 
150
 
 
151
        if (!error && input_tree_names.empty()) error = "no input trees specified";
 
152
 
 
153
        if (!error) {
 
154
            size_t    species_count;
 
155
            char     *comment;
 
156
            GBT_TREE *cons_tree = build_consensus_tree(input_tree_names, error, species_count, 1.0, comment);
 
157
 
 
158
            if (!cons_tree) {
 
159
                error = GBS_global_string("Failed to build consensus tree (Reason: %s)", error);
 
160
            }
 
161
            else {
 
162
                size_t leafs   = GBT_count_leafs(cons_tree);
 
163
                double percent = size_t((leafs*1000)/species_count)/10.0;
 
164
 
 
165
                printf("Generated tree contains %.1f%% of species (%zu of %zu found in input trees)\n",
 
166
                       percent, leafs, species_count);
 
167
 
 
168
                if (savename) {
 
169
                    error = save_tree_as_newick(cons_tree, savename, comment);
 
170
                }
 
171
                else {
 
172
                    printf("successfully created consensus tree\n"
 
173
                           "(no savename specified -> tree not saved)\n");
 
174
                }
 
175
                delete cons_tree;
 
176
            }
 
177
            free(comment);
 
178
        }
 
179
        free(savename);
 
180
    }
 
181
 
 
182
    if (error) {
 
183
        printf("Error in arb_consensus_tree: %s\n", error);
 
184
    }
 
185
 
 
186
    return error ? EXIT_FAILURE : EXIT_SUCCESS;
 
187
}
 
188
 
 
189
// --------------------------------------------------------------------------------
 
190
 
 
191
#ifdef UNIT_TESTS
 
192
#ifndef TEST_UNIT_H
 
193
#include <test_unit.h>
 
194
#endif
 
195
 
 
196
#include "command_output.h"
 
197
 
 
198
// #define TEST_AUTO_UPDATE // uncomment to update expected trees (if more than date differs)
 
199
 
 
200
static char *custom_tree_name(int dir, const char *name) { return GBS_global_string_copy("consense/%i/%s.tree", dir, name); }
 
201
static char *custom_numbered_tree_name(int dir, const char *name, int treeNr) { return GBS_global_string_copy("consense/%i/%s_%i.tree", dir, name, treeNr); }
 
202
 
 
203
static void add_inputnames(StrArray& to, int dir, const char *basename, int first_tree, int last_tree) {
 
204
    for (int t = first_tree; t <= last_tree; ++t) {
 
205
        to.put(custom_numbered_tree_name(dir, basename, t));
 
206
    }
 
207
}
 
208
 
 
209
static double calc_intree_distance(GBT_TREE *tree) {
 
210
    if (tree->is_leaf) return 0.0;
 
211
    return
 
212
        tree->leftlen +
 
213
        tree->rightlen +
 
214
        calc_intree_distance(tree->leftson) +
 
215
        calc_intree_distance(tree->rightson);
 
216
}
 
217
 
 
218
#define LENSUM_EPSILON .000001
 
219
 
 
220
static arb_test::match_expectation consense_tree_generated(GBT_TREE *tree, GB_ERROR error, size_t species_count, size_t expected_species_count, double expected_intree_distance) {
 
221
    using namespace   arb_test;
 
222
    expectation_group expected;
 
223
 
 
224
    expected.add(that(error).is_equal_to_NULL());
 
225
    expected.add(that(tree).does_differ_from_NULL());
 
226
 
 
227
    if (tree) {
 
228
        expected.add(that(species_count).is_equal_to(expected_species_count));
 
229
        expected.add(that(GBT_count_leafs(tree)).is_equal_to(expected_species_count));
 
230
        expected.add(that(calc_intree_distance(tree)).fulfills(epsilon_similar(LENSUM_EPSILON), expected_intree_distance));
 
231
    }
 
232
 
 
233
    return all().ofgroup(expected);
 
234
}
 
235
 
 
236
static arb_test::match_expectation build_expected_consensus_tree(const int treedir, const char *basename, int first_tree, int last_tree, double weight, const char *outbasename, size_t expected_species_count, double expected_intree_distance) {
 
237
    using namespace       arb_test;
 
238
    expectation_group     expected;
 
239
    arb_suppress_progress hideProgress;
 
240
 
 
241
    GB_ERROR  error   = NULL;
 
242
    StrArray  input_tree_names;
 
243
    add_inputnames(input_tree_names, treedir, basename, first_tree, last_tree);
 
244
 
 
245
    size_t    species_count;
 
246
    char     *comment;
 
247
    GBT_TREE *tree = build_consensus_tree(input_tree_names, error, species_count, weight, comment);
 
248
    expected.add(consense_tree_generated(tree, error, species_count, expected_species_count, expected_intree_distance));
 
249
 
 
250
    char *saveas = custom_tree_name(treedir, outbasename);
 
251
    error        = save_tree_as_newick(tree, saveas, comment);
 
252
    expected.add(that(error).is_equal_to_NULL());
 
253
 
 
254
    if (!error) {
 
255
        char *expected_save        = custom_tree_name(treedir, GBS_global_string("%s_expected", outbasename));
 
256
        bool  exported_as_expected = arb_test::textfiles_have_difflines_ignoreDates(expected_save, saveas, 0);
 
257
 
 
258
#if defined(TEST_AUTO_UPDATE)
 
259
        if (!exported_as_expected) {
 
260
            ASSERT_RESULT(int, 0, system(GBS_global_string("cp %s %s", saveas, expected_save)));
 
261
        }
 
262
#else // !defined(TEST_AUTO_UPDATE)
 
263
        expected.add(that(exported_as_expected).is_equal_to(true));
 
264
#endif
 
265
        TEST_EXPECT_ZERO_OR_SHOW_ERRNO(GB_unlink(saveas));
 
266
        free(expected_save);
 
267
    }
 
268
 
 
269
    free(saveas);
 
270
    free(comment);
 
271
    delete tree;
 
272
 
 
273
    return all().ofgroup(expected);
 
274
}
 
275
 
 
276
void TEST_consensus_tree_1() {
 
277
    TEST_EXPECTATION(build_expected_consensus_tree(1, "bootstrapped", 1, 5, 0.7, "consense1", 22, 0.925779));
 
278
    // ../UNIT_TESTER/run/consense/1/consense1.tree
 
279
}
 
280
void TEST_consensus_tree_1_single() {
 
281
    TEST_EXPECTATION(build_expected_consensus_tree(1, "bootstrapped", 1, 1, 0.01, "consense1_single", 22, 0.924610));
 
282
    // ../UNIT_TESTER/run/consense/1/consense1_single.tree
 
283
}
 
284
 
 
285
void TEST_consensus_tree_2() {
 
286
    TEST_EXPECTATION(build_expected_consensus_tree(2, "bootstrapped", 1, 4, 2.5, "consense2", 59, 2.849827));
 
287
    // ../UNIT_TESTER/run/consense/2/consense2.tree
 
288
}
 
289
 
 
290
void TEST_consensus_tree_3() {
 
291
    TEST_EXPECTATION(build_expected_consensus_tree(3, "bootstrapped", 1, 3, 137.772, "consense3", 128, 2.170685));
 
292
    // ../UNIT_TESTER/run/consense/3/consense3.tree
 
293
}
 
294
 
 
295
void TEST_consensus_tree_from_disjunct_trees() {
 
296
    TEST_EXPECTATION(build_expected_consensus_tree(4, "disjunct", 1, 2, 137.772, "disjunct_merged", 15, 2.034290));
 
297
    // ../UNIT_TESTER/run/consense/4/disjunct_merged.tree
 
298
}
 
299
 
 
300
void TEST_consensus_tree_from_partly_overlapping_trees() {
 
301
    // tree_disjunct_3 contains 7 species
 
302
    // (3 from upper subtree (tree_disjunct_1) and 4 from lower subtree (tree_disjunct_2))
 
303
 
 
304
    TEST_EXPECTATION(build_expected_consensus_tree(4, "disjunct", 1, 3, 137.772, "overlap_merged", 15, 2.596455));
 
305
    // ../UNIT_TESTER/run/consense/4/overlap_merged.tree
 
306
}
 
307
 
 
308
void TEST_consensus_tree_from_minimal_overlapping_trees() {
 
309
    // tree_disjunct_0 only contains 2 species (1 from upper and 1 from lower subtree).
 
310
    TEST_EXPECTATION(build_expected_consensus_tree(4, "disjunct", 0, 2, 137.772, "overlap_mini_merged", 15, 2.750745));
 
311
    // ../UNIT_TESTER/run/consense/4/overlap_mini_merged.tree
 
312
}
 
313
 
 
314
void TEST_consensus_tree_described_in_arbhelp() {
 
315
    // see ../HELP_SOURCE/oldhelp/consense_algo.hlp
 
316
    TEST_EXPECTATION(build_expected_consensus_tree(5, "help", 1, 2, 2.0, "help_merged", 6, 1.050000));
 
317
    // ../UNIT_TESTER/run/consense/5/help_merged.tree
 
318
}
 
319
 
 
320
void TEST_consensus_tree_from_trees_overlapping_by_twothirds() {
 
321
    // These 3 trees where copied from an existing tree.
 
322
    // From each copy one third of all species has been removed
 
323
    // (removed sets were disjunct)
 
324
    TEST_EXPECTATION(build_expected_consensus_tree(6, "overlap_two_thirds", 1, 3, 19.2, "overlap_twothirds_merged", 15, 3.561680));
 
325
    // ../UNIT_TESTER/run/consense/6/overlap_twothirds_merged.tree
 
326
}
 
327
 
 
328
void TEST_consensus_tree_from_mostly_overlapping_trees() {
 
329
    // the 3 trees were copied from tree_disjunct_source.
 
330
    // from each tree 2 (different) species were deleted.
 
331
    TEST_EXPECTATION(build_expected_consensus_tree(7, "disjunct_del2", 1, 3, 137.772, "overlap_mostly", 15, 1.820057));
 
332
    // ../UNIT_TESTER/run/consense/7/overlap_mostly.tree
 
333
}
 
334
 
 
335
void TEST_consensus_tree_from_mostly_overlapping_trees_2() {
 
336
    // the 3 trees were copied from tree_disjunct1
 
337
    // from each tree 1 (different) species was deleted.
 
338
    TEST_EXPECTATION(build_expected_consensus_tree(8, "overlap2", 1, 3, 137.772, "overlap2_mostly", 8, 0.529109));
 
339
    // ../UNIT_TESTER/run/consense/8/overlap2_mostly.tree
 
340
}
 
341
 
 
342
 
 
343
 
 
344
#define REPEATED_TESTS
 
345
 
 
346
#if defined(REPEATED_TESTS)
 
347
void TEST_consensus_tree_generation_is_deterministic() {
 
348
    TEST_consensus_tree_described_in_arbhelp();
 
349
    TEST_consensus_tree_from_minimal_overlapping_trees();
 
350
    TEST_consensus_tree_from_partly_overlapping_trees();
 
351
    TEST_consensus_tree_from_disjunct_trees();
 
352
    TEST_consensus_tree_3();
 
353
    TEST_consensus_tree_2();
 
354
    TEST_consensus_tree_1_single();
 
355
    TEST_consensus_tree_1();
 
356
}
 
357
 
 
358
void TEST_arb_consensus_tree() {
 
359
    TEST_STDOUT_CONTAINS("(arb_consensus_tree -x || true)", "Unknown switch '-x'");
 
360
    TEST_STDOUT_CONTAINS("(arb_consensus_tree -w sth || true)", "no input trees specified");
 
361
 
 
362
    {
 
363
        char *saveas   = custom_tree_name(1, "consense1");
 
364
        char *expected = custom_tree_name(1, "consense1_expected");
 
365
    
 
366
        TEST_STDOUT_CONTAINS("arb_consensus_tree"
 
367
                             " -w consense/1/consense1.tree"
 
368
                             " consense/1/bootstrapped_1.tree"
 
369
                             " consense/1/bootstrapped_2.tree"
 
370
                             " consense/1/bootstrapped_3.tree"
 
371
                             " consense/1/bootstrapped_4.tree"
 
372
                             " consense/1/bootstrapped_5.tree"
 
373
                             ,
 
374
                             "database  created");
 
375
 
 
376
        TEST_EXPECT_TEXTFILE_DIFFLINES_IGNORE_DATES(saveas, expected, 0);
 
377
        TEST_EXPECT_ZERO_OR_SHOW_ERRNO(GB_unlink(saveas));
 
378
 
 
379
        free(expected);
 
380
        free(saveas);
 
381
    }
 
382
 
 
383
    {
 
384
        char *saveas   = custom_tree_name(2, "consense2");
 
385
        char *expected = custom_tree_name(2, "consense2_expected");
 
386
    
 
387
        TEST_STDOUT_CONTAINS("arb_consensus_tree"
 
388
                             " -w consense/2/consense2.tree"
 
389
                             " consense/2/bootstrapped_1.tree"
 
390
                             " consense/2/bootstrapped_2.tree"
 
391
                             " consense/2/bootstrapped_3.tree"
 
392
                             " consense/2/bootstrapped_4.tree"
 
393
                             ,
 
394
                             "database  created");
 
395
 
 
396
        TEST_EXPECT_TEXTFILE_DIFFLINES_IGNORE_DATES(saveas, expected, 0);
 
397
        TEST_EXPECT_ZERO_OR_SHOW_ERRNO(GB_unlink(saveas));
 
398
 
 
399
        free(expected);
 
400
        free(saveas);
 
401
    }
 
402
}
 
403
#endif // REPEATED_TESTS
 
404
 
 
405
// #define TREEIO_AUTO_UPDATE // uncomment to auto-update expected test-results
 
406
// #define TREEIO_AUTO_UPDATE_IF_EXPORT_DIFFERS // uncomment to auto-update expected test-results
 
407
// #define TREEIO_AUTO_UPDATE_IF_REEXPORT_DIFFERS // uncomment to auto-update expected test-results
 
408
 
 
409
static const char *findFirstNameContaining(GBT_TREE *tree, const char *part) {
 
410
    const char *found = NULL;
 
411
    if (tree->name && strstr(tree->name, part)) {
 
412
        found = tree->name;
 
413
    }
 
414
    else if (!tree->is_leaf) {
 
415
        found             = findFirstNameContaining(tree->leftson, part);
 
416
        if (!found) found = findFirstNameContaining(tree->rightson, part);
 
417
    }
 
418
    return found;
 
419
}
 
420
 
 
421
void TEST_SLOW_treeIO_stable() {
 
422
    const char *dbname   = "trees/bootstrap_groups.arb";
 
423
    const char *treename = "tree_bootstrap_and_groups";
 
424
    const char *savename = "bg";
 
425
 
 
426
    GB_shell  shell;
 
427
    GBDATA   *gb_main = GB_open(dbname, "rw");
 
428
 
 
429
    TEST_REJECT_NULL(gb_main);
 
430
 
 
431
    GBT_TREE_NodeFactory nodeMaker;
 
432
 
 
433
    char *outfile = GBS_global_string_copy("trees/%s.tree", savename);
 
434
 
 
435
    for (int save_branchlengths = 0; save_branchlengths <= 1; ++save_branchlengths) {
 
436
        for (int save_bootstraps = 0; save_bootstraps <= 1; ++save_bootstraps) {
 
437
            for (int save_groupnames = 0; save_groupnames <= 1; ++save_groupnames) {
 
438
                bool quoting_occurs = save_bootstraps && save_groupnames;
 
439
                for (int pretty = 0; pretty <= 1; ++pretty) {
 
440
 
 
441
                    for (int quoting = TREE_DISALLOW_QUOTES; quoting <= (quoting_occurs ? TREE_DOUBLE_QUOTES : TREE_DISALLOW_QUOTES); ++quoting) {
 
442
                        TREE_node_quoting quoteMode = TREE_node_quoting(quoting);
 
443
 
 
444
                        char *paramID = GBS_global_string_copy("%s_%s%s%s_%i",
 
445
                                                               pretty ? "p" : "s",
 
446
                                                               save_bootstraps ? "Bs" : "",
 
447
                                                               save_groupnames ? "Grp" : "",
 
448
                                                               save_branchlengths ? "Len" : "",
 
449
                                                               quoteMode);
 
450
 
 
451
                        TEST_ANNOTATE(GBS_global_string("for paramID='%s'", paramID));
 
452
 
 
453
                        GB_ERROR export_error = TREE_write_Newick(gb_main, treename, NULL, save_branchlengths, save_bootstraps, save_groupnames, pretty, quoteMode, outfile);
 
454
                        TEST_EXPECT_NULL(export_error);
 
455
 
 
456
                        char *expectedfile = GBS_global_string_copy("trees/%s_exp_%s.tree", savename, paramID);
 
457
 
 
458
#if defined(TREEIO_AUTO_UPDATE)
 
459
                        system(GBS_global_string("cp %s %s", outfile, expectedfile));
 
460
#else // !defined(TREEIO_AUTO_UPDATE)
 
461
                        bool exported_as_expected = arb_test::textfiles_have_difflines_ignoreDates(expectedfile, outfile, 0);
 
462
#if defined(TREEIO_AUTO_UPDATE_IF_EXPORT_DIFFERS)
 
463
                        if (!exported_as_expected) {
 
464
                            system(GBS_global_string("cp %s %s", outfile, expectedfile));
 
465
                        }
 
466
#else // !defined(TREEIO_AUTO_UPDATE_IF_EXPORT_DIFFERS)
 
467
                        TEST_EXPECT(exported_as_expected);
 
468
#endif
 
469
 
 
470
                        // reimport exported tree
 
471
                        const char *reloaded_treename = "tree_reloaded";
 
472
                        {
 
473
                            char     *comment    = NULL;
 
474
                            GBT_TREE *tree       = TREE_load(expectedfile, nodeMaker, &comment, true, NULL);
 
475
                            GB_ERROR  load_error = tree ? NULL : GB_await_error();
 
476
 
 
477
                            TEST_EXPECTATION(all().of(that(tree).does_differ_from_NULL(),
 
478
                                                      that(load_error).is_equal_to_NULL()));
 
479
                            // store tree in DB
 
480
                            {
 
481
                                GB_transaction ta(gb_main);
 
482
                                GB_ERROR       store_error = GBT_write_tree_with_remark(gb_main, reloaded_treename, tree, comment);
 
483
                                TEST_EXPECT_NULL(store_error);
 
484
                            }
 
485
                            free(comment);
 
486
 
 
487
                            if (save_groupnames) {
 
488
                                const char *quotedGroup     = findFirstNameContaining(tree, "quoted");
 
489
                                const char *underscoreGroup = findFirstNameContaining(tree, "bs100");
 
490
                                TEST_EXPECT_EQUAL(quotedGroup, "quoted");
 
491
                                TEST_EXPECT_EQUAL(underscoreGroup, "__bs100");
 
492
                            }
 
493
                            const char *capsLeaf = findFirstNameContaining(tree, "Caps");
 
494
                            TEST_EXPECT_EQUAL(capsLeaf, "_MhuCaps");
 
495
 
 
496
                            delete tree;
 
497
                        }
 
498
 
 
499
                        // export again
 
500
                        GB_ERROR reexport_error = TREE_write_Newick(gb_main, reloaded_treename, NULL, save_branchlengths, save_bootstraps, save_groupnames, pretty, quoteMode, outfile);
 
501
                        TEST_EXPECT_NULL(reexport_error);
 
502
 
 
503
                        // eliminate comments added by loading/saving
 
504
                        char *outfile2 = GBS_global_string_copy("trees/%s2.tree", savename);
 
505
                        {
 
506
                            char *cmd = GBS_global_string_copy("cat %s"
 
507
                                                               " | grep -v 'Loaded from trees/.*_exp_'"
 
508
                                                               " | grep -v 'tree_reloaded saved to'"
 
509
                                                               " > %s", outfile, outfile2);
 
510
                            TEST_EXPECT_NO_ERROR(GBK_system(cmd));
 
511
                            free(cmd);
 
512
                        }
 
513
 
 
514
                        bool reexported_as_expected = arb_test::textfiles_have_difflines(expectedfile, outfile2, 0);
 
515
 
 
516
#if defined(TREEIO_AUTO_UPDATE_IF_REEXPORT_DIFFERS)
 
517
                        if (!reexported_as_expected) {
 
518
                            system(GBS_global_string("cp %s %s", outfile2, expectedfile));
 
519
                        }
 
520
#else // !defined(TREEIO_AUTO_UPDATE_IF_REEXPORT_DIFFERS)
 
521
                        TEST_EXPECT(reexported_as_expected);
 
522
#endif
 
523
 
 
524
                        TEST_EXPECT_ZERO_OR_SHOW_ERRNO(unlink(outfile2));
 
525
                        free(outfile2);
 
526
#endif
 
527
                        free(expectedfile);
 
528
                        free(paramID);
 
529
                    }
 
530
                }
 
531
            }
 
532
        }
 
533
    }
 
534
    TEST_ANNOTATE(NULL);
 
535
 
 
536
    TEST_EXPECT_ZERO_OR_SHOW_ERRNO(unlink(outfile));
 
537
    free(outfile);
 
538
 
 
539
    GB_close(gb_main);
 
540
}
 
541
 
 
542
void TEST_CONSENSUS_TREE_functionality() {
 
543
    // functionality wanted in RootedTree (for use in library CONSENSUS_TREE)
 
544
 
 
545
    char *comment = NULL;
 
546
 
 
547
    SizeAwareTree *tree = DOWNCAST(SizeAwareTree*, TREE_load("trees/bg_exp_p_GrpLen_0.tree",
 
548
                                                             *new TreeRoot(new SizeAwareNodeFactory, true),
 
549
                                                             &comment, false, NULL));
 
550
    // -> ../UNIT_TESTER/run/trees/bg_exp_p_GrpLen_0.tree
 
551
 
 
552
#define ORG_1111 "(AticSea6,(RblAerol,RblMesop))"
 
553
#define TOP_1111 "((RblAerol,RblMesop),AticSea6)"
 
554
#define BOT_1111 ORG_1111
 
555
 
 
556
#define ORG_11121 "((DnrShiba,RsbElon4),MmbAlkal)"
 
557
#define TOP_11121 ORG_11121
 
558
#define BOT_11121 "(MmbAlkal,(DnrShiba,RsbElon4))"
 
559
 
 
560
#define ORG_11122 "((MabPelag,MabSalin),PaoMaris)"
 
561
#define TOP_11122 ORG_11122
 
562
#define BOT_11122 "(PaoMaris,(MabPelag,MabSalin))"
 
563
 
 
564
#define ORG_1112 "(" ORG_11121 "," ORG_11122 ")"
 
565
#define TOP_1112 "(" TOP_11121 "," TOP_11122 ")"
 
566
#define BOT_1112 "(" BOT_11121 "," BOT_11122 ")"
 
567
#define EDG_1112 "(" TOP_11121 "," BOT_11122 ")"
 
568
 
 
569
#define ORG_111 "(" ORG_1111 "," ORG_1112 ")"
 
570
#define TOP_111 "(" TOP_1112 "," TOP_1111 ")"
 
571
#define BOT_111 "(" BOT_1111 "," BOT_1112 ")"
 
572
#define EDG_111 "(" EDG_1112 "," BOT_1111 ")"
 
573
 
 
574
#define ORG_112 "(OnlGran2,RsnAnta2)"
 
575
#define TOP_112 ORG_112
 
576
#define BOT_112 ORG_112
 
577
 
 
578
#define ORG_11 "(" ORG_111 "," ORG_112 ")"
 
579
#define TOP_11 "(" TOP_111 "," TOP_112 ")"
 
580
#define BOT_11 "(" BOT_112 "," BOT_111 ")"
 
581
#define EDG_11 "(" EDG_111 "," BOT_112 ")"
 
582
 
 
583
#define ORG_12 "(_MhuCaps,ThtNivea)"
 
584
#define TOP_12 "(ThtNivea,_MhuCaps)"
 
585
#define BOT_12 TOP_12
 
586
 
 
587
#define ORG_1 "(" ORG_11 "," ORG_12 ")"
 
588
#define TOP_1 "(" TOP_11 "," TOP_12 ")"
 
589
#define BOT_1 "(" BOT_12 "," BOT_11 ")"
 
590
#define EDG_1 "(" EDG_11 "," BOT_12 ")"
 
591
 
 
592
#define ORG_2 "((LbnMarin,LbnzAlb4),LbnAlexa)"
 
593
#define TOP_2 ORG_2
 
594
#define BOT_2 "(LbnAlexa,(LbnMarin,LbnzAlb4))"
 
595
 
 
596
    // test swap_sons
 
597
    TEST_ASSERT_VALID_TREE(tree);
 
598
    TEST_EXPECT_NEWICK(nSIMPLE, tree, "(" ORG_1 "," ORG_2 ");");
 
599
    tree->swap_sons();
 
600
    TEST_EXPECT_NEWICK(nSIMPLE, tree, "(" ORG_2 "," ORG_1 ");");
 
601
 
 
602
    // test reorder_tree
 
603
    TEST_ASSERT_VALID_TREE(tree);
 
604
    TreeOrder order[] = { BIG_BRANCHES_TO_TOP, BIG_BRANCHES_TO_BOTTOM, BIG_BRANCHES_TO_EDGE };
 
605
 
 
606
    for (size_t o1 = 0; o1<ARRAY_ELEMS(order); ++o1) {
 
607
        TreeOrder to_order = order[o1];
 
608
        for (size_t o2 = 0; o2<ARRAY_ELEMS(order); ++o2) {
 
609
            TreeOrder from_order = order[o2];
 
610
 
 
611
            for (int rotate = 0; rotate<=1; ++rotate) {
 
612
                tree->reorder_tree(from_order);
 
613
                if (rotate) tree->rotate_subtree();
 
614
                tree->reorder_tree(to_order);
 
615
 
 
616
                switch (to_order) {
 
617
                    case BIG_BRANCHES_TO_TOP:    TEST_EXPECT_NEWICK(nSIMPLE, tree, "(" TOP_1 "," TOP_2 ");"); break;
 
618
                    case BIG_BRANCHES_TO_EDGE:   TEST_EXPECT_NEWICK(nSIMPLE, tree, "(" EDG_1 "," BOT_2 ");"); break;
 
619
                    case BIG_BRANCHES_TO_BOTTOM: TEST_EXPECT_NEWICK(nSIMPLE, tree, "(" BOT_2 "," BOT_1 ");"); break;
 
620
                    default: TEST_REJECT(true); break;
 
621
                }
 
622
 
 
623
            }
 
624
        }
 
625
    }
 
626
 
 
627
    // test rotate_subtree
 
628
    TEST_ASSERT_VALID_TREE(tree);
 
629
    tree->reorder_tree(BIG_BRANCHES_TO_TOP);
 
630
    tree->rotate_subtree(); TEST_EXPECT_NEWICK(nSIMPLE, tree, "((LbnAlexa,(LbnzAlb4,LbnMarin)),((_MhuCaps,ThtNivea),((RsnAnta2,OnlGran2),((AticSea6,(RblMesop,RblAerol)),((PaoMaris,(MabSalin,MabPelag)),(MmbAlkal,(RsbElon4,DnrShiba)))))));");
 
631
    tree->rotate_subtree(); TEST_EXPECT_NEWICK(nSIMPLE, tree, "(" TOP_1 "," TOP_2 ");");
 
632
 
 
633
 
 
634
    // test set_root
 
635
    TEST_ASSERT_VALID_TREE(tree);
 
636
    RootedTree *AticSea6Grandpa = tree->findLeafNamed("AticSea6")->get_father()->get_father();
 
637
    TEST_REJECT_NULL(AticSea6Grandpa);
 
638
    TEST_ASSERT_VALID_TREE(AticSea6Grandpa);
 
639
 
 
640
    AticSea6Grandpa->set_root();
 
641
    TEST_EXPECT_NEWICK(nSIMPLE, tree,
 
642
                             "((" ORG_1112 "," TOP_1111 ")," // AticSea6 is direct son of TOP_1111
 
643
                             "((" ORG_2 "," TOP_12 ")," ORG_112 "));");
 
644
 
 
645
    // test auto-detection of "best" root
 
646
    TEST_ASSERT_VALID_TREE(tree);
 
647
    tree->get_tree_root()->find_innermost_edge().set_root();
 
648
    TEST_EXPECT_NEWICK(nLENGTH, tree,
 
649
                             "((((LbnMarin:0.019,LbnzAlb4:0.003):0.016,LbnAlexa:0.032):0.122,(ThtNivea:0.230,_MhuCaps:0.194):0.427):0.076,"
 
650
                             "(((((DnrShiba:0.076,RsbElon4:0.053):0.034,MmbAlkal:0.069):0.016,((MabPelag:0.001,MabSalin:0.009):0.095,PaoMaris:0.092):0.036):0.030,((RblAerol:0.085,RblMesop:0.042):0.238,AticSea6:0.111):0.018):0.036,(OnlGran2:0.057,RsnAnta2:0.060):0.021):0.076);");
 
651
 
 
652
    TEST_ASSERT_VALID_TREE(tree);
 
653
    delete tree;
 
654
    free(comment);
 
655
}
 
656
 
 
657
#endif // UNIT_TESTS
 
658
 
 
659
// --------------------------------------------------------------------------------