source: tags/ms_r18q1/DIST/DI_matr.cxx

Last change on this file was 17110, checked in by westram, 6 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 65.3 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : DI_matr.cxx                                       //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "di_protdist.hxx"
12#include "di_clusters.hxx"
13#include "dist.hxx"
14#include "di_view_matrix.hxx"
15#include "di_awars.hxx"
16
17#include <neighbourjoin.hxx>
18#include <AP_filter.hxx>
19#include <CT_ctree.hxx>
20#include <ColumnStat.hxx>
21
22#include <awt.hxx>
23#include <awt_sel_boxes.hxx>
24#include <awt_filter.hxx>
25
26#include <aw_preset.hxx>
27#include <aw_awars.hxx>
28#include <aw_file.hxx>
29#include <aw_msg.hxx>
30#include <aw_root.hxx>
31
32#include <gui_aliview.hxx>
33
34#include <climits>
35#include <ctime>
36#include <cmath>
37#include <arb_sort.h>
38#include <arb_global_defs.h>
39#include <macros.hxx>
40#include <ad_cb.h>
41#include <awt_TreeAwars.hxx>
42#include <arb_defs.h>
43
44using std::string;
45
46// --------------------------------------------------------------------------------
47
48#define AWAR_DIST_BOOTSTRAP_COUNT    AWAR_DIST_PREFIX "bootstrap/count"
49#define AWAR_DIST_CANCEL_CHARS       AWAR_DIST_PREFIX "cancel/chars"
50#define AWAR_DIST_MATRIX_AUTO_RECALC AWAR_DIST_PREFIX "recalc"
51
52#define AWAR_DIST_COLUMN_STAT_NAME AWAR_DIST_COLUMN_STAT_PREFIX "name"
53
54#define AWAR_DIST_TREE_SORT_NAME        "tmp/" AWAR_DIST_TREE_PREFIX "sort_tree_name"
55#define AWAR_DIST_TREE_COMP_NAME        "tmp/" AWAR_DIST_TREE_PREFIX "compr_tree_name"
56#define AWAR_DIST_TREE_STD_NAME         AWAR_DIST_TREE_PREFIX "tree_name"
57#define AWAR_DIST_MATRIX_AUTO_CALC_TREE AWAR_DIST_TREE_PREFIX "autocalc"
58
59#define AWAR_DIST_SAVE_MATRIX_TYPE     AWAR_DIST_SAVE_MATRIX_BASE "/type"
60#define AWAR_DIST_SAVE_MATRIX_FILENAME AWAR_DIST_SAVE_MATRIX_BASE "/file_name"
61
62// --------------------------------------------------------------------------------
63
64DI_GLOBAL_MATRIX GLOBAL_MATRIX;
65
66static MatrixDisplay     *matrixDisplay = NULp;
67static AP_userdef_matrix  userdef_DNA_matrix(AP_MAX, AWAR_DIST_MATRIX_DNA_BASE);
68
69static AP_matrix *get_user_matrix() {
70    AW_root *awr = AW_root::SINGLETON;
71    if (!awr->awar(AWAR_DIST_MATRIX_DNA_ENABLED)->read_int()) {
72        return NULp;
73    }
74    userdef_DNA_matrix.update_from_awars(awr);
75    userdef_DNA_matrix.normize();
76    return &userdef_DNA_matrix;
77}
78
79class BoundWindowCallback : virtual Noncopyable {
80    AW_window      *aww;
81    WindowCallback  cb;
82public:
83    BoundWindowCallback(AW_window *aww_, const WindowCallback& cb_)
84        : aww(aww_),
85          cb(cb_)
86    {}
87    void operator()() { cb(aww); }
88};
89
90static SmartPtr<BoundWindowCallback> recalculate_matrix_cb;
91static SmartPtr<BoundWindowCallback> recalculate_tree_cb;
92
93static GB_ERROR last_matrix_calculation_error = NULp;
94
95static void matrix_changed_cb() {
96    if (matrixDisplay) {
97        matrixDisplay->mark(MatrixDisplay::NEED_SETUP);
98        matrixDisplay->update_display();
99    }
100}
101
102struct RecalcNeeded {
103    bool matrix;
104    bool tree;
105
106    RecalcNeeded() : matrix(false), tree(false) { }
107};
108
109static RecalcNeeded need_recalc;
110
111CONSTEXPR unsigned UPDATE_DELAY = 200;
112
113static unsigned update_cb(AW_root *aw_root);
114inline void add_update_cb() {
115    AW_root::SINGLETON->add_timed_callback(UPDATE_DELAY, makeTimedCallback(update_cb));
116}
117
118inline void matrix_needs_recalc_cb() {
119    need_recalc.matrix = true;
120    add_update_cb();
121}
122inline void tree_needs_recalc_cb() {
123    need_recalc.tree = true;
124    add_update_cb();
125}
126
127inline void compressed_matrix_needs_recalc_cb() {
128    if (GLOBAL_MATRIX.has_type(DI_MATRIX_COMPRESSED)) {
129        matrix_needs_recalc_cb();
130    }
131}
132
133static unsigned update_cb(AW_root *aw_root) {
134    if (need_recalc.matrix) {
135        GLOBAL_MATRIX.forget();
136        need_recalc.matrix = false; // because it's forgotten
137
138        int matrix_autocalc = aw_root->awar(AWAR_DIST_MATRIX_AUTO_RECALC)->read_int();
139        if (matrix_autocalc) {
140            bool recalc_now    = true;
141            int  tree_autocalc = aw_root->awar(AWAR_DIST_MATRIX_AUTO_CALC_TREE)->read_int();
142            if (!tree_autocalc) recalc_now = matrixDisplay ? matrixDisplay->willShow() : false;
143
144            if (recalc_now) {
145                di_assert(recalculate_matrix_cb.isSet());
146                (*recalculate_matrix_cb)();
147                di_assert(need_recalc.tree == true);
148            }
149        }
150        di_assert(need_recalc.matrix == false);
151    }
152
153    if (need_recalc.tree) {
154        int tree_autocalc = aw_root->awar(AWAR_DIST_MATRIX_AUTO_CALC_TREE)->read_int();
155        if (tree_autocalc) {
156            di_assert(recalculate_tree_cb.isSet());
157            (*recalculate_tree_cb)();
158            need_recalc.matrix = false; // otherwise endless loop, e.g. if output-tree is used for sorting
159        }
160    }
161
162    return 0; // do not call again
163}
164
165static void auto_calc_changed_cb(AW_root *aw_root) {
166    int matrix_autocalc = aw_root->awar(AWAR_DIST_MATRIX_AUTO_RECALC)->read_int();
167    int tree_autocalc   = aw_root->awar(AWAR_DIST_MATRIX_AUTO_CALC_TREE)->read_int();
168
169    if (matrix_autocalc && !GLOBAL_MATRIX.exists()) matrix_needs_recalc_cb();
170    if (tree_autocalc && (matrix_autocalc || GLOBAL_MATRIX.exists())) tree_needs_recalc_cb();
171}
172
173static AW_window *create_dna_matrix_window(AW_root *aw_root) {
174    AW_window_simple *aws = new AW_window_simple;
175    aws->init(aw_root, "SET_DNA_MATRIX", "SET MATRIX");
176    aws->auto_increment(50, 50);
177    aws->button_length(10);
178    aws->callback(AW_POPDOWN);
179    aws->create_button("CLOSE", "CLOSE");
180
181    aws->callback(makeHelpCallback("user_matrix.hlp"));
182    aws->create_button("HELP", "HELP");
183
184    aws->at_newline();
185
186    userdef_DNA_matrix.create_input_fields(aws);
187    aws->window_fit();
188    return aws;
189}
190
191static void selected_tree_changed_cb() {
192    if (AW_root::SINGLETON->awar(AWAR_DIST_CORR_TRANS)->read_int() == DI_TRANSFORMATION_FROM_TREE) {
193        matrix_needs_recalc_cb();
194    }
195}
196
197void DI_create_matrix_variables(AW_root *aw_root, AW_default def, AW_default db) {
198    GB_transaction ta(db);
199
200    userdef_DNA_matrix.set_descriptions(AP_A,   "A");
201    userdef_DNA_matrix.set_descriptions(AP_C,   "C");
202    userdef_DNA_matrix.set_descriptions(AP_G,   "G");
203    userdef_DNA_matrix.set_descriptions(AP_T,   "TU");
204    userdef_DNA_matrix.set_descriptions(AP_GAP, "GAP");
205
206    userdef_DNA_matrix.create_awars(aw_root);
207
208    RootCallback matrix_needs_recalc_callback = makeRootCallback(matrix_needs_recalc_cb);
209    aw_root->awar_int(AWAR_DIST_MATRIX_DNA_ENABLED, 0)->add_callback(matrix_needs_recalc_callback); // user matrix disabled by default
210    {
211        GBDATA *gbd = GB_search(AW_ROOT_DEFAULT, AWAR_DIST_MATRIX_DNA_BASE, GB_FIND);
212        GB_add_callback(gbd, GB_CB_CHANGED, makeDatabaseCallback(matrix_needs_recalc_cb));
213    }
214
215    aw_root->awar_string(AWAR_DIST_WHICH_SPECIES, "marked", def)->add_callback(matrix_needs_recalc_callback);
216    {
217        char *default_ali = GBT_get_default_alignment(db);
218        aw_root->awar_string(AWAR_DIST_ALIGNMENT, "", def)->add_callback(matrix_needs_recalc_callback)->write_string(default_ali);
219        free(default_ali);
220    }
221    aw_root->awar_string(AWAR_DIST_FILTER_ALIGNMENT, "none", def);
222    aw_root->awar_string(AWAR_DIST_FILTER_NAME,      "none", def);
223    aw_root->awar_string(AWAR_DIST_FILTER_FILTER,    "",     def)->add_callback(matrix_needs_recalc_callback);
224    aw_root->awar_int   (AWAR_DIST_FILTER_SIMPLIFY,  0,      def)->add_callback(matrix_needs_recalc_callback);
225
226    aw_root->awar_string(AWAR_DIST_CANCEL_CHARS, ".", def)->add_callback(matrix_needs_recalc_callback);
227    aw_root->awar_int(AWAR_DIST_CORR_TRANS, (int)DI_TRANSFORMATION_SIMILARITY, def)->add_callback(matrix_needs_recalc_callback)->set_minmax(0, DI_TRANSFORMATION_COUNT-1);
228
229    aw_root->awar(AWAR_DIST_FILTER_ALIGNMENT)->map(AWAR_DIST_ALIGNMENT);
230
231    AW_create_fileselection_awars(aw_root, AWAR_DIST_SAVE_MATRIX_BASE, ".", "", "infile");
232    aw_root->awar_int(AWAR_DIST_SAVE_MATRIX_TYPE, 0, def);
233
234    enum treetype { CURR, SORT, COMPRESS, TREEAWARCOUNT };
235    AW_awar *tree_awar[TREEAWARCOUNT] = { NULp, NULp, NULp };
236
237    aw_root->awar_string(AWAR_DIST_TREE_STD_NAME,  "tree_nj", def);
238    {
239        char *currentTree = aw_root->awar_string(AWAR_TREE, "", db)->read_string();
240        tree_awar[CURR]   = aw_root->awar_string(AWAR_DIST_TREE_CURR_NAME, currentTree, def);
241        tree_awar[SORT]   = aw_root->awar_string(AWAR_DIST_TREE_SORT_NAME, currentTree, def);
242        free(currentTree);
243    }
244    tree_awar[COMPRESS] = aw_root->awar_string(AWAR_DIST_TREE_COMP_NAME, NO_TREE_SELECTED, def);
245
246    aw_root->awar_int(AWAR_DIST_BOOTSTRAP_COUNT, 1000, def);
247    aw_root->awar_int(AWAR_DIST_MATRIX_AUTO_RECALC, 0, def)->add_callback(auto_calc_changed_cb);
248    aw_root->awar_int(AWAR_DIST_MATRIX_AUTO_CALC_TREE, 0, def)->add_callback(auto_calc_changed_cb);
249
250    aw_root->awar_float(AWAR_DIST_MIN_DIST, 0.0)->set_minmax(0.0, 4.0);
251    aw_root->awar_float(AWAR_DIST_MAX_DIST, 0.0)->set_minmax(0.0, 4.0);
252
253    aw_root->awar_string(AWAR_SPECIES_NAME, "", db);
254
255    DI_create_cluster_awars(aw_root, def, db);
256
257#if defined(DEBUG)
258    AWT_create_db_browser_awars(aw_root, def);
259#endif // DEBUG
260
261    {
262        GB_push_transaction(db);
263
264        GBDATA *gb_species_data = GBT_get_species_data(db);
265        GB_add_callback(gb_species_data, GB_CB_CHANGED, makeDatabaseCallback(matrix_needs_recalc_cb));
266
267        GB_pop_transaction(db);
268    }
269
270    AWT_registerTreeAwarCallback(tree_awar[CURR],     makeTreeAwarCallback(selected_tree_changed_cb),          true);
271    AWT_registerTreeAwarCallback(tree_awar[SORT],     makeTreeAwarCallback(matrix_needs_recalc_cb),            true);
272    AWT_registerTreeAwarCallback(tree_awar[COMPRESS], makeTreeAwarCallback(compressed_matrix_needs_recalc_cb), true);
273
274    auto_calc_changed_cb(aw_root);
275}
276
277DI_ENTRY::DI_ENTRY(GBDATA *gbd, DI_MATRIX *phmatrix_)
278    : phmatrix(phmatrix_),
279      full_name(NULp),
280      sequence(NULp),
281      name(NULp),
282      group_nr(0)
283{
284    GBDATA *gb_ali = GB_entry(gbd, phmatrix->get_aliname());
285    if (gb_ali) {
286        GBDATA *gb_data = GB_entry(gb_ali, "data");
287        if (gb_data) {
288            if (phmatrix->is_AA) {
289                sequence = new AP_sequence_simple_protein(phmatrix->get_aliview());
290            }
291            else {
292                sequence = new AP_sequence_parsimony(phmatrix->get_aliview());
293            }
294            sequence->bind_to_species(gbd);
295            sequence->lazy_load_sequence(); // load sequence
296
297            name      = GBT_read_string(gbd, "name");
298            full_name = GBT_read_string(gbd, "full_name");
299        }
300    }
301}
302
303DI_ENTRY::DI_ENTRY(const char *name_, DI_MATRIX *phmatrix_)
304    : phmatrix(phmatrix_),
305      full_name(NULp),
306      sequence(NULp),
307      name(ARB_strdup(name_)),
308      group_nr(0)
309{}
310
311DI_ENTRY::~DI_ENTRY() {
312    delete sequence;
313    free(name);
314    free(full_name);
315}
316
317DI_MATRIX::DI_MATRIX(const AliView& aliview_)
318    : gb_species_data(NULp),
319      seq_len(0),
320      allocated_entries(0),
321      aliview(new AliView(aliview_)),
322      is_AA(false),
323      entries(NULp),
324      nentries(0),
325      matrix(NULp),
326      matrix_type(DI_MATRIX_FULL)
327{
328    memset(cancel_columns, 0, sizeof(cancel_columns));
329}
330
331char *DI_MATRIX::unload() {
332    for (size_t i=0; i<nentries; i++) {
333        delete entries[i];
334    }
335    freenull(entries);
336    nentries = 0;
337    return NULp;
338}
339
340DI_MATRIX::~DI_MATRIX() {
341    unload();
342    delete matrix;
343    delete aliview;
344}
345
346struct TreeOrderedSpecies {
347    GBDATA  *gbd;
348    int      order_index;
349
350    TreeOrderedSpecies(const MatrixOrder& order, GBDATA *gb_spec)
351        : gbd(gb_spec),
352          order_index(order.get_index(GBT_read_name(gbd)))
353    {}
354};
355
356MatrixOrder::MatrixOrder(GBDATA *gb_main, GB_CSTR sort_tree_name)
357    : name2pos(NULp),
358      leafs(0)
359{
360    if (sort_tree_name) {
361        int       size;
362        TreeNode *sort_tree = GBT_read_tree_and_size(gb_main, sort_tree_name, new SimpleRoot, &size);
363
364        if (sort_tree) {
365            leafs    = size+1;
366            name2pos = GBS_create_hash(leafs, GB_IGNORE_CASE);
367
368            IF_ASSERTION_USED(int leafsLoaded = leafs);
369            leafs = 0;
370            insert_in_hash(sort_tree);
371
372            arb_assert(leafsLoaded == leafs);
373            destroy(sort_tree);
374        }
375        else {
376            GB_clear_error();
377        }
378    }
379}
380static int TreeOrderedSpecies_cmp(const void *p1, const void *p2, void *) {
381    TreeOrderedSpecies *s1 = (TreeOrderedSpecies*)p1;
382    TreeOrderedSpecies *s2 = (TreeOrderedSpecies*)p2;
383
384    return s2->order_index - s1->order_index;
385}
386
387void MatrixOrder::applyTo(TreeOrderedSpecies **species_array, size_t array_size) const {
388    GB_sort((void**)species_array, 0, array_size, TreeOrderedSpecies_cmp, NULp);
389}
390
391GB_ERROR DI_MATRIX::load(LoadWhat what, const MatrixOrder& order, bool show_warnings, GBDATA **species_list) {
392    GBDATA     *gb_main = get_gb_main();
393    const char *use     = get_aliname();
394
395    GB_transaction ta(gb_main);
396
397    seq_len          = GBT_get_alignment_len(gb_main, use);
398    is_AA            = GBT_is_alignment_protein(gb_main, use);
399    gb_species_data  = GBT_get_species_data(gb_main);
400
401    allocated_entries = 1000;
402    ARB_calloc(entries, allocated_entries);
403
404    nentries = 0;
405
406    size_t no_of_species = -1U;
407    switch (what) {
408        case DI_LOAD_ALL:
409            no_of_species = GBT_get_species_count(gb_main);
410            break;
411        case DI_LOAD_MARKED:
412            no_of_species = GBT_count_marked_species(gb_main);
413            break;
414        case DI_LOAD_LIST:
415            di_assert(species_list);
416            for (no_of_species = 0; species_list[no_of_species]; ++no_of_species) ;
417            break;
418    }
419
420    di_assert(no_of_species != -1U);
421    if (no_of_species<2) {
422        return GBS_global_string("Not enough input species (%zu)", no_of_species);
423    }
424
425    TreeOrderedSpecies *species_to_load[no_of_species];
426
427    {
428        size_t i = 0;
429        switch (what) {
430            case DI_LOAD_ALL: {
431                for (GBDATA *gb_species = GBT_first_species_rel_species_data(gb_species_data); gb_species; gb_species = GBT_next_species(gb_species), ++i) {
432                    species_to_load[i] = new TreeOrderedSpecies(order, gb_species);
433                }
434                break;
435            }
436            case DI_LOAD_MARKED: {
437                for (GBDATA *gb_species = GBT_first_marked_species_rel_species_data(gb_species_data); gb_species; gb_species = GBT_next_marked_species(gb_species), ++i) {
438                    species_to_load[i] = new TreeOrderedSpecies(order, gb_species);
439                }
440                break;
441            }
442            case DI_LOAD_LIST: {
443                for (i = 0; species_list[i]; ++i) {
444                    species_to_load[i] = new TreeOrderedSpecies(order, species_list[i]);
445                }
446                break;
447            }
448        }
449        arb_assert(i == no_of_species);
450    }
451
452    if (order.defined()) {
453        order.applyTo(species_to_load, no_of_species);
454        if (show_warnings) {
455            int species_not_in_sort_tree = 0;
456            for (size_t i = 0; i<no_of_species; ++i) {
457                if (!species_to_load[i]->order_index) {
458                    species_not_in_sort_tree++;
459                }
460            }
461            if (species_not_in_sort_tree) {
462                GBT_message(gb_main, GBS_global_string("Warning: %i of the affected species are not in sort-tree", species_not_in_sort_tree));
463            }
464        }
465    }
466    else {
467        if (show_warnings) {
468            static bool shown = false;
469            if (!shown) { // showing once is enough
470                GBT_message(gb_main, "Warning: No valid tree given to sort matrix (using default database order)");
471                shown = true;
472            }
473        }
474    }
475
476    if (no_of_species>allocated_entries) {
477        allocated_entries = no_of_species;
478        ARB_realloc(entries, allocated_entries);
479    }
480
481    GB_ERROR     error = NULp;
482    arb_progress progress("Preparing sequence data", no_of_species);
483    for (size_t i = 0; i<no_of_species && !error; ++i) {
484        DI_ENTRY *phentry = new DI_ENTRY(species_to_load[i]->gbd, this);
485        if (phentry->sequence) {    // a species found
486            arb_assert(nentries<allocated_entries);
487            entries[nentries++] = phentry;
488        }
489        else {
490            delete phentry;
491        }
492        delete species_to_load[i];
493        species_to_load[i] = NULp;
494
495        progress.inc_and_check_user_abort(error);
496    }
497
498    return error;
499}
500
501char *DI_MATRIX::calculate_overall_freqs(double rel_frequencies[AP_MAX], char *cancel) {
502    di_assert(is_AA == false);
503
504    long hits2[AP_MAX];
505    long sum   = 0;
506    int  i;
507    int  pos;
508    int  b;
509    long s_len = aliview->get_length();
510
511    memset((char *) &hits2[0], 0, sizeof(hits2));
512    for (size_t row = 0; row < nentries; row++) {
513        const char *seq1 = entries[row]->get_nucl_seq()->get_sequence();
514        for (pos = 0; pos < s_len; pos++) {
515            b = *(seq1++);
516            if (cancel[b]) continue;
517            hits2[b]++;
518        }
519    }
520    for (i = 0; i < AP_MAX; i++) { // LOOP_VECTORIZED
521        sum += hits2[i];
522    }
523    for (i = 0; i < AP_MAX; i++) {
524        rel_frequencies[i] = hits2[i] / (double) sum;
525    }
526    return NULp;
527}
528
529double DI_MATRIX::corr(double dist, double b, double & sigma) {
530    const double eps = 0.01;
531    double ar = 1.0 - dist/b;
532    sigma = 1000.0;
533    if (ar< eps) return 3.0;
534    sigma = b/ar;
535    return - b * log(1-dist/b);
536}
537
538GB_ERROR DI_MATRIX::calculate(const char *cancel, DI_TRANSFORMATION transformation, bool *aborted_flag, AP_matrix *userdef_matrix) {
539    di_assert(is_AA == false);
540
541    if (userdef_matrix) {
542        switch (transformation) {
543            case DI_TRANSFORMATION_NONE:
544            case DI_TRANSFORMATION_SIMILARITY:
545            case DI_TRANSFORMATION_JUKES_CANTOR:
546                break;
547            default:
548                aw_message("Sorry: this kind of distance correction does not support a user defined matrix - it will be ignored");
549                userdef_matrix = NULp;
550                break;
551        }
552    }
553
554    matrix = new AP_smatrix(nentries);
555
556    long   s_len = aliview->get_length();
557    long   hits[AP_MAX][AP_MAX];
558    size_t i;
559
560    if (nentries<=1) {
561        return "Not enough species selected to calculate matrix";
562    }
563    memset(&cancel_columns[0], 0, 256);
564
565    for (i=0; cancel[i]; i++) {
566        cancel_columns[safeCharIndex(AP_sequence_parsimony::table[safeCharIndex(cancel[i])])] = 1;
567        UNCOVERED(); // @@@ cover
568    }
569
570    long   columns;
571    double b;
572    long   frequencies[AP_MAX];
573    double rel_frequencies[AP_MAX];
574    double S_square = 0;
575
576    switch (transformation) {
577        case DI_TRANSFORMATION_FELSENSTEIN:
578            this->calculate_overall_freqs(rel_frequencies, cancel_columns);
579            S_square = 0.0;
580            for (i=0; i<AP_MAX; i++) S_square += rel_frequencies[i]*rel_frequencies[i];
581            break;
582        default:    break;
583    };
584
585    arb_progress progress("Calculating distance matrix", matrix_halfsize(nentries, true));
586    GB_ERROR     error = NULp;
587    for (size_t row = 0; row<nentries && !error; row++) {
588        for (size_t col=0; col<=row && !error; col++) {
589            columns = 0;
590
591            const unsigned char *seq1 = entries[row]->get_nucl_seq()->get_usequence();
592            const unsigned char *seq2 = entries[col]->get_nucl_seq()->get_usequence();
593
594            b = 0.0;
595            switch (transformation) {
596                case  DI_TRANSFORMATION_FROM_TREE:
597                    di_assert(0);
598                    break;
599                case  DI_TRANSFORMATION_JUKES_CANTOR:
600                    b = 0.75;
601                    // fall-through
602                case  DI_TRANSFORMATION_NONE:
603                case  DI_TRANSFORMATION_SIMILARITY: {
604                    double  dist = 0.0;
605                    if (userdef_matrix) {
606                        memset((char *)hits, 0, sizeof(long) * AP_MAX * AP_MAX);
607                        int pos;
608                        for (pos = s_len; pos >= 0; pos--) {
609                            hits[*(seq1++)][*(seq2++)]++;
610                        }
611                        int x, y;
612                        double diffsum = 0.0;
613                        double all_sum = 0.001;
614                        for (x = AP_A; x < AP_MAX; x*=2) {
615                            for (y = AP_A; y < AP_MAX; y*=2) {
616                                if (x==y) {
617                                    all_sum += hits[x][y];
618                                }
619                                else {
620                                    UNCOVERED(); // @@@ cover
621                                    diffsum += hits[x][y] * userdef_matrix->get(x, y);
622                                    all_sum += hits[x][y] * userdef_matrix->get(x, y);
623                                }
624                            }
625                        }
626                        dist = diffsum / all_sum;
627                    }
628                    else {
629                        for (int pos = s_len; pos >= 0; pos--) {
630                            int b1 = *(seq1++);
631                            int b2 = *(seq2++);
632                            if (cancel_columns[b1]) continue;
633                            if (cancel_columns[b2]) continue;
634                            columns++;
635                            if (b1&b2) continue;
636                            dist+=1.0;
637                        }
638                        if (columns == 0) columns = 1;
639                        dist /= columns;
640                    }
641                    if (transformation==DI_TRANSFORMATION_SIMILARITY) {
642                        dist =  (1.0-dist);
643                    }
644                    else if (b) {
645                        double sigma;
646                        dist = this->corr(dist, b, sigma);
647                    }
648                    matrix->set(row, col, dist);
649                    break;
650                }
651                case DI_TRANSFORMATION_KIMURA:
652                case DI_TRANSFORMATION_OLSEN:
653                case DI_TRANSFORMATION_FELSENSTEIN: {
654                    int    pos;
655                    double dist = 0.0;
656                    long   N, P, Q, M;
657                    double p, q;
658
659                    memset((char *)hits, 0, sizeof(long) * AP_MAX * AP_MAX);
660                    for (pos = s_len; pos >= 0; pos--) {
661                        hits[*(seq1++)][*(seq2++)]++;
662                    }
663                    switch (transformation) {
664                        case DI_TRANSFORMATION_KIMURA:
665                            P = hits[AP_A][AP_G] +
666                                hits[AP_G][AP_A] +
667                                hits[AP_C][AP_T] +
668                                hits[AP_T][AP_C];
669                            Q = hits[AP_A][AP_C] +
670                                hits[AP_A][AP_T] +
671                                hits[AP_C][AP_A] +
672                                hits[AP_T][AP_A] +
673                                hits[AP_G][AP_C] +
674                                hits[AP_G][AP_T] +
675                                hits[AP_C][AP_G] +
676                                hits[AP_T][AP_G];
677                            M = hits[AP_A][AP_A] +
678                                hits[AP_C][AP_C] +
679                                hits[AP_G][AP_G] +
680                                hits[AP_T][AP_T];
681                            N = P+Q+M;
682                            if (N==0) N=1;
683                            p = (double)P/(double)N;
684                            q = (double)Q/(double)N;
685                            dist = - .5 * log(
686                                              (1.0-2.0*p-q)*sqrt(1.0-2.0*q)
687                                              );
688                            break;
689
690                        case DI_TRANSFORMATION_OLSEN:
691                        case DI_TRANSFORMATION_FELSENSTEIN:
692
693                            memset((char *)frequencies, 0,
694                                   sizeof(long) * AP_MAX);
695
696                            N = 0;
697                            M = 0;
698
699                            for (i=0; i<AP_MAX; i++) {
700                                if (cancel_columns[i]) continue;
701                                unsigned int j;
702                                for (j=0; j<i; j++) {
703                                    if (cancel_columns[j]) continue;
704                                    frequencies[i] +=
705                                        hits[i][j]+
706                                        hits[j][i];
707                                }
708                                frequencies[i] += hits[i][i];
709                                N += frequencies[i];
710                                M += hits[i][i];
711                            }
712                            if (N==0) N=1;
713                            if (transformation == DI_TRANSFORMATION_OLSEN) { // Calc sum square freq individually for each line
714                                S_square = 0.0;
715                                for (i=0; i<AP_MAX; i++) S_square += frequencies[i]*frequencies[i];
716                                b = 1.0 - S_square/((double)N*(double)N);
717                            }
718                            else {
719                                b = 1.0 - S_square;
720                            }
721
722                            dist = ((double)(N-M)) / (double) N;
723                            double sigma;
724                            dist = this->corr(dist, b, sigma);
725                            break;
726
727                        default: return "Sorry: Transformation not implemented";
728                    }
729                    matrix->set(row, col, dist);
730                    break;
731                }
732                default:;
733            }   // switch
734            progress.inc_and_check_user_abort(error);
735        }
736    }
737    if (aborted_flag && progress.aborted()) *aborted_flag = true;
738    return error;
739}
740
741GB_ERROR DI_MATRIX::calculate_pro(DI_TRANSFORMATION transformation, bool *aborted_flag) {
742    di_assert(is_AA == true);
743
744    di_cattype catType;
745    switch (transformation) {
746        case DI_TRANSFORMATION_NONE:                catType = NONE;       break;
747        case DI_TRANSFORMATION_SIMILARITY:          catType = SIMILARITY; break;
748        case DI_TRANSFORMATION_KIMURA:              catType = KIMURA;     break;
749        case DI_TRANSFORMATION_PAM:                 catType = PAM;        break;
750        case DI_TRANSFORMATION_CATEGORIES_HALL:     catType = HALL;       break;
751        case DI_TRANSFORMATION_CATEGORIES_BARKER:   catType = GEORGE;     break;
752        case DI_TRANSFORMATION_CATEGORIES_CHEMICAL: catType = CHEMICAL;   break;
753        default:
754            return "This correction is not available for protein data";
755    }
756    matrix = new AP_smatrix(nentries);
757
758    di_protdist prodist(UNIVERSAL, catType, nentries, entries, aliview->get_length(), matrix);
759    return prodist.makedists(aborted_flag);
760}
761
762typedef std::map<const char*, TreeNode*, charpLess> NamedNodes;
763
764GB_ERROR link_to_tree(NamedNodes& named, TreeNode *node) {
765    GB_ERROR error = NULp;
766    if (node->is_leaf()) {
767        NamedNodes::iterator found = named.find(node->name);
768        if (found != named.end()) {
769            if (found->second) {
770                error = GBS_global_string("Invalid tree (two nodes named '%s')", node->name);
771            }
772            else {
773                found->second = node;
774            }
775        }
776        // otherwise, we do not care about the node (e.g. because it is not marked)
777    }
778    else {
779        error             = link_to_tree(named, node->get_leftson());
780        if (!error) error = link_to_tree(named, node->get_rightson());
781    }
782    return error;
783}
784
785#if defined(ASSERTION_USED)
786static TreeNode *findNode(TreeNode *node, const char *name) {
787    if (node->is_leaf()) {
788        return strcmp(node->name, name) == 0 ? node : NULp;
789    }
790
791    TreeNode *found   = findNode(node->get_leftson(), name);
792    if (!found) found = findNode(node->get_rightson(), name);
793    return found;
794}
795#endif
796
797static GB_ERROR init(NamedNodes& node, TreeNode *tree, const DI_ENTRY*const*const entries, size_t nentries) {
798    GB_ERROR error = NULp;
799    for (size_t n = 0; n<nentries; ++n) {
800        node[entries[n]->name] = NULp;
801    }
802    error = link_to_tree(node, tree);
803    if (!error) { // check for missing species (needed but not in tree)
804        size_t      missing     = 0;
805        const char *exampleName = NULp;
806
807        for (size_t n = 0; n<nentries; ++n) {
808            NamedNodes::iterator found = node.find(entries[n]->name);
809            if (found == node.end()) {
810                ++missing;
811                exampleName = entries[n]->name;
812            }
813            else {
814                di_assert(node[entries[n]->name] == findNode(tree, entries[n]->name));
815                if (!node[entries[n]->name]) {
816                    ++missing;
817                    exampleName = entries[n]->name;
818                }
819            }
820        }
821
822        if (missing) {
823            error = GBS_global_string("Tree is missing %zu required species (e.g. '%s')", missing, exampleName);
824        }
825    }
826    return error;
827}
828
829GB_ERROR DI_MATRIX::extract_from_tree(const char *treename, bool *aborted_flag) {
830    GB_ERROR error         = NULp;
831    if (nentries<=1) error = "Not enough species selected to calculate matrix";
832    else {
833        TreeNode *tree;
834        {
835            GB_transaction ta(get_gb_main());
836            tree = GBT_read_tree(get_gb_main(), treename, new SimpleRoot);
837        }
838        if (!tree) error = GB_await_error();
839        else {
840            arb_progress progress("Extracting distances from tree", matrix_halfsize(nentries, true));
841            NamedNodes   node;
842
843            error  = init(node, tree, entries, nentries);
844            matrix = new AP_smatrix(nentries);
845
846            for (size_t row = 0; row<nentries && !error; row++) {
847                TreeNode *rnode = node[entries[row]->name];
848                for (size_t col=0; col<=row && !error; col++) {
849                    double dist;
850                    if (col != row) {
851                        TreeNode *cnode = node[entries[col]->name];
852                        dist  = rnode->intree_distance_to(cnode);
853                    }
854                    else {
855                        dist = 0.0;
856                    }
857                    matrix->set(row, col, dist);
858                    progress.inc_and_check_user_abort(error);
859                }
860            }
861            UNCOVERED();
862            destroy(tree);
863            if (aborted_flag && progress.aborted()) *aborted_flag = true;
864            if (error) progress.done();
865        }
866    }
867    return error;
868}
869
870__ATTR__USERESULT static GB_ERROR di_calculate_matrix(AW_root *aw_root, const WeightedFilter *weighted_filter, bool bootstrap_flag, bool show_warnings, bool *aborted_flag) {
871    // sets 'aborted_flag' to true, if it is non-NULp and the calculation has been aborted
872    GB_ERROR error = NULp;
873
874    if (GLOBAL_MATRIX.exists()) {
875        di_assert(!need_recalc.matrix);
876    }
877    else {
878        GB_transaction ta(GLOBAL_gb_main);
879
880        char *use     = aw_root->awar(AWAR_DIST_ALIGNMENT)->read_string();
881        long  ali_len = GBT_get_alignment_len(GLOBAL_gb_main, use);
882
883        if (ali_len<=0) {
884            error = "Please select a valid alignment";
885            GB_clear_error();
886        }
887        else {
888            arb_progress  progress("Calculating matrix");
889            char         *cancel  = aw_root->awar(AWAR_DIST_CANCEL_CHARS)->read_string();
890            AliView      *aliview = weighted_filter->create_aliview(use, error);
891
892            if (!error) {
893                if (bootstrap_flag) aliview->get_filter()->enable_bootstrap();
894
895                char *load_what      = aw_root->awar(AWAR_DIST_WHICH_SPECIES)->read_string();
896                char *sort_tree_name = aw_root->awar(AWAR_DIST_TREE_SORT_NAME)->read_string();
897
898                LoadWhat all_flag = (strcmp(load_what, "all") == 0) ? DI_LOAD_ALL : DI_LOAD_MARKED;
899                {
900                    DI_MATRIX *phm   = new DI_MATRIX(*aliview);
901                    phm->matrix_type = DI_MATRIX_FULL;
902
903                    static SmartCharPtr          last_sort_tree_name;
904                    static SmartPtr<MatrixOrder> last_order;
905
906                    if (last_sort_tree_name.isNull() || !sort_tree_name || strcmp(&*last_sort_tree_name, sort_tree_name) != 0) {
907                        last_sort_tree_name = nulldup(sort_tree_name);
908                        last_order = new MatrixOrder(GLOBAL_gb_main, sort_tree_name);
909                    }
910                    di_assert(last_order.isSet());
911                    error = phm->load(all_flag, *last_order, show_warnings, NULp);
912
913                    free(sort_tree_name);
914                    error = ta.close(error);
915
916                    bool aborted = false;
917                    if (!error) {
918                        if (progress.aborted()) {
919                            phm->unload();
920                            error   = "Aborted by user";
921                            aborted = true;
922                        }
923                        else {
924                            DI_TRANSFORMATION trans = (DI_TRANSFORMATION)aw_root->awar(AWAR_DIST_CORR_TRANS)->read_int();
925
926                            if (trans == DI_TRANSFORMATION_FROM_TREE) {
927                                const char *treename = aw_root->awar(AWAR_DIST_TREE_CURR_NAME)->read_char_pntr();
928                                error                = phm->extract_from_tree(treename, &aborted);
929                            }
930                            else {
931                                if (phm->is_AA) error = phm->calculate_pro(trans, &aborted);
932                                else            error = phm->calculate(cancel, trans, &aborted, get_user_matrix());
933                            }
934                        }
935                    }
936
937                    if (aborted) {
938                        di_assert(error);
939                        if (aborted_flag) *aborted_flag = true;
940                    }
941                    if (error) {
942                        delete phm;
943                        GLOBAL_MATRIX.forget();
944                    }
945                    else {
946                        GLOBAL_MATRIX.replaceBy(phm);
947                        tree_needs_recalc_cb();
948                        need_recalc.matrix = false;
949                    }
950                }
951                free(load_what);
952            }
953
954            free(cancel);
955            delete aliview;
956        }
957        free(use);
958
959        di_assert(contradicted(error, GLOBAL_MATRIX.exists()));
960    }
961    return error;
962}
963
964static void di_mark_by_distance(AW_window *aww, WeightedFilter *weighted_filter) {
965    AW_root *aw_root    = aww->get_root();
966    float    lowerBound = aw_root->awar(AWAR_DIST_MIN_DIST)->read_float();
967    float    upperBound = aw_root->awar(AWAR_DIST_MAX_DIST)->read_float();
968
969    GB_ERROR error = NULp;
970    if (lowerBound >= upperBound) {
971        error = GBS_global_string("Lower bound (%f) has to be smaller than upper bound (%f)", lowerBound, upperBound);
972    }
973    else if (lowerBound<0.0 || lowerBound > 1.0) {
974        error = GBS_global_string("Lower bound (%f) is not in allowed range [0.0 .. 1.0]", lowerBound);
975    }
976    else if (upperBound<0.0 || upperBound > 1.0) {
977        error = GBS_global_string("Upper bound (%f) is not in allowed range [0.0 .. 1.0]", upperBound);
978    }
979    else {
980        GB_transaction ta(GLOBAL_gb_main);
981
982        char *selected = aw_root->awar(AWAR_SPECIES_NAME)->read_string();
983        if (!selected[0]) {
984            error = "Please select a species";
985        }
986        else {
987            GBDATA *gb_selected = GBT_find_species(GLOBAL_gb_main, selected);
988            if (!gb_selected) {
989                error = GBS_global_string("Couldn't find species '%s'", selected);
990            }
991            else {
992                char              *use     = aw_root->awar(AWAR_DIST_ALIGNMENT)->read_string();
993                char              *cancel  = aw_root->awar(AWAR_DIST_CANCEL_CHARS)->read_string();
994                DI_TRANSFORMATION  trans   = (DI_TRANSFORMATION)aw_root->awar(AWAR_DIST_CORR_TRANS)->read_int();
995                AliView           *aliview = weighted_filter->create_aliview(use, error);
996
997                if (!error) {
998                    DI_MATRIX *prev_global = GLOBAL_MATRIX.swap(NULp);
999
1000                    size_t speciesCount   = GBT_get_species_count(GLOBAL_gb_main);
1001                    bool   markedSelected = false;
1002
1003                    arb_progress progress("Mark species by distance", speciesCount);
1004                    MatrixOrder  order(GLOBAL_gb_main, NULp);
1005
1006                    for (GBDATA *gb_species = GBT_first_species(GLOBAL_gb_main);
1007                         gb_species && !error;
1008                         gb_species = GBT_next_species(gb_species))
1009                    {
1010                        DI_MATRIX *phm         = new DI_MATRIX(*aliview);
1011                        phm->matrix_type       = DI_MATRIX_FULL;
1012                        GBDATA *species_pair[] = { gb_selected, gb_species, NULp };
1013
1014                        error = phm->load(DI_LOAD_LIST, order, false, species_pair);
1015
1016                        if (phm->nentries == 2) { // if species has no alignment -> nentries<2
1017                            if (!error) {
1018                                if (phm->is_AA) error = phm->calculate_pro(trans, NULp);
1019                                else            error = phm->calculate(cancel, trans, NULp, get_user_matrix());
1020                            }
1021
1022                            if (!error) {
1023                                double dist_value = phm->matrix->get(0, 1);                         // distance or conformance
1024                                bool   mark       = (lowerBound <= dist_value && dist_value <= upperBound);
1025                                GB_write_flag(gb_species, mark);
1026
1027                                if (!markedSelected) {
1028                                    dist_value = phm->matrix->get(0, 0);                                     // distance or conformance to self
1029                                    mark       = (lowerBound <= dist_value && dist_value <= upperBound);
1030                                    GB_write_flag(gb_selected, mark);
1031
1032                                    markedSelected = true;
1033                                }
1034                            }
1035                        }
1036
1037                        delete phm;
1038                        if (!error) progress.inc_and_check_user_abort(error);
1039                    }
1040
1041                    di_assert(!GLOBAL_MATRIX.exists());
1042                    ASSERT_RESULT(DI_MATRIX*, NULp, GLOBAL_MATRIX.swap(prev_global));
1043
1044                    if (error) progress.done();
1045                }
1046
1047                delete aliview;
1048                free(cancel);
1049                free(use);
1050            }
1051        }
1052
1053        free(selected);
1054        error = ta.close(error);
1055    }
1056
1057    if (error) {
1058        aw_message(error);
1059    }
1060}
1061
1062static GB_ERROR di_recalc_matrix() {
1063    // recalculate matrix
1064    last_matrix_calculation_error = NULp;
1065    if (need_recalc.matrix && GLOBAL_MATRIX.exists()) {
1066        GLOBAL_MATRIX.forget();
1067    }
1068    di_assert(recalculate_matrix_cb.isSet());
1069    (*recalculate_matrix_cb)();
1070    return last_matrix_calculation_error;
1071}
1072
1073static void di_view_matrix_cb(AW_window *aww, save_matrix_params *sparam) {
1074    GB_ERROR error = di_recalc_matrix();
1075    if (error) return;
1076
1077    if (!matrixDisplay) matrixDisplay = new MatrixDisplay;
1078
1079    static AW_window *viewer = NULp;
1080    if (!viewer) viewer = DI_create_view_matrix_window(aww->get_root(), matrixDisplay, sparam);
1081
1082    matrixDisplay->mark(MatrixDisplay::NEED_SETUP);
1083    matrixDisplay->update_display();
1084
1085    GLOBAL_MATRIX.set_changed_cb(matrix_changed_cb);
1086
1087    viewer->activate();
1088}
1089
1090static void di_save_matrix_cb(AW_window *aww) {
1091    // save the matrix
1092    GB_ERROR error = di_recalc_matrix();
1093    if (!error) {
1094        char              *filename = aww->get_root()->awar(AWAR_DIST_SAVE_MATRIX_FILENAME)->read_string();
1095        enum DI_SAVE_TYPE  type     = (enum DI_SAVE_TYPE)aww->get_root()->awar(AWAR_DIST_SAVE_MATRIX_TYPE)->read_int();
1096
1097        GLOBAL_MATRIX.get()->save(filename, type);
1098        free(filename);
1099    }
1100    AW_refresh_fileselection(aww->get_root(), AWAR_DIST_SAVE_MATRIX_BASE);
1101    aww->hide_or_notify(error);
1102}
1103
1104AW_window *DI_create_save_matrix_window(AW_root *aw_root, save_matrix_params *save_params) {
1105    static AW_window_simple *aws = NULp;
1106    if (!aws) {
1107        aws = new AW_window_simple;
1108        aws->init(aw_root, "SAVE_MATRIX", "Save Matrix");
1109        aws->load_xfig("sel_box_user.fig");
1110
1111        aws->at("close");
1112        aws->callback(AW_POPDOWN);
1113        aws->create_button("CLOSE", "CANCEL", "C");
1114
1115
1116        aws->at("help"); aws->callback(makeHelpCallback("save_matrix.hlp"));
1117        aws->create_button("HELP", "HELP", "H");
1118
1119        aws->at("user");
1120        aws->create_option_menu(AWAR_DIST_SAVE_MATRIX_TYPE, true);
1121        aws->insert_default_option("Phylip Format (Lower Triangular Matrix)", "P", DI_SAVE_PHYLIP_COMP);
1122        aws->insert_option("Readable (using NDS)", "R", DI_SAVE_READABLE);
1123        aws->insert_option("Tabbed (using NDS)", "R", DI_SAVE_TABBED);
1124        aws->update_option_menu();
1125
1126        AW_create_standard_fileselection(aws, save_params->awar_base);
1127
1128        aws->at("save2");
1129        aws->callback(makeWindowCallback(di_save_matrix_cb));
1130        aws->create_button("SAVE", "SAVE", "S");
1131
1132        aws->at("cancel2");
1133        aws->callback(AW_POPDOWN);
1134        aws->create_button("CLOSE", "CANCEL", "C");
1135    }
1136    return aws;
1137}
1138
1139static AW_window *awt_create_select_cancel_window(AW_root *aw_root) {
1140    AW_window_simple *aws = new AW_window_simple;
1141    aws->init(aw_root, "SELECT_CHARS_TO_CANCEL_COLUMN", "CANCEL SELECT");
1142    aws->load_xfig("di_cancel.fig");
1143
1144    aws->at("close");
1145    aws->callback(AW_POPDOWN);
1146    aws->create_button("CLOSE", "CLOSE", "C");
1147
1148    aws->at("cancel");
1149    aws->create_input_field(AWAR_DIST_CANCEL_CHARS, 12);
1150
1151    return aws;
1152}
1153
1154static const char *enum_trans_to_string[] = {
1155    "none",
1156    "similarity",
1157    "jukes_cantor",
1158    "felsenstein",
1159
1160    "pam",
1161    "hall",
1162    "barker",
1163    "chemical",
1164
1165    "kimura",
1166    "olsen",
1167    "felsenstein voigt",
1168    "olsen voigt",
1169    "max ml",
1170
1171    NULp, // treedist
1172};
1173
1174STATIC_ASSERT(ARRAY_ELEMS(enum_trans_to_string) == DI_TRANSFORMATION_COUNT);
1175
1176static void di_calculate_tree_cb(AW_window *aww, WeightedFilter *weighted_filter, bool bootstrap_flag) {
1177    recalculate_tree_cb = new BoundWindowCallback(aww, makeWindowCallback(di_calculate_tree_cb, weighted_filter, bootstrap_flag));
1178
1179    AW_root  *aw_root   = aww->get_root();
1180    GB_ERROR  error     = NULp;
1181    StrArray *all_names = NULp;
1182
1183    int loop_count      = 0;
1184    int bootstrap_count = aw_root->awar(AWAR_DIST_BOOTSTRAP_COUNT)->read_int();
1185
1186    {
1187        char *tree_name = aw_root->awar(AWAR_DIST_TREE_STD_NAME)->read_string();
1188        error           = GBT_check_tree_name(tree_name);
1189        free(tree_name);
1190    }
1191
1192    SmartPtr<arb_progress>  progress;
1193    SmartPtr<ConsensusTree> ctree;
1194
1195    if (!error) {
1196        if (bootstrap_flag) {
1197            if (bootstrap_count) {
1198                progress = new arb_progress("Calculating bootstrap trees", bootstrap_count+1);
1199            }
1200            else {
1201                progress = new arb_progress("Calculating bootstrap trees (KILL to stop)", INT_MAX);
1202            }
1203            progress->auto_subtitles("tree");
1204        }
1205        else {
1206            progress = new arb_progress("Calculating tree");
1207        }
1208
1209        if (bootstrap_flag) {
1210            GLOBAL_MATRIX.forget();
1211            GLOBAL_MATRIX.set_changed_cb(NULp); // otherwise matrix window will repeatedly pop up/down
1212
1213            error = di_calculate_matrix(aw_root, weighted_filter, bootstrap_flag, true, NULp);
1214            if (!error) {
1215                DI_MATRIX *matr = GLOBAL_MATRIX.get();
1216                if (!matr) {
1217                    error = "unexpected error in di_calculate_matrix_cb (data missing)";
1218                }
1219                else {
1220                    all_names = new StrArray;
1221                    all_names->reserve(matr->nentries+2);
1222
1223                    for (size_t i=0; i<matr->nentries; i++) {
1224                        all_names->put(ARB_strdup(matr->entries[i]->name));
1225                    }
1226                    ctree = new ConsensusTree(*all_names);
1227                }
1228            }
1229        }
1230    }
1231
1232    TreeNode *tree = NULp;
1233    do {
1234        if (error) break;
1235
1236        bool aborted = false;
1237
1238        if (bootstrap_flag) {
1239            if (loop_count>0) { // in first loop we already have a valid matrix -> no need to recalculate
1240                GLOBAL_MATRIX.forget();
1241            }
1242        }
1243        else if (need_recalc.matrix) {
1244            GLOBAL_MATRIX.forget();
1245        }
1246
1247        error = di_calculate_matrix(aw_root, weighted_filter, bootstrap_flag, !bootstrap_flag, &aborted);
1248        if (error && aborted) {
1249            error = NULp;       // clear error (otherwise no tree will be read below)
1250            break;              // end of bootstrap
1251        }
1252
1253        if (!GLOBAL_MATRIX.exists()) {
1254            error = "unexpected error in di_calculate_matrix_cb (data missing)";
1255            break;
1256        }
1257
1258        DI_MATRIX  *matr  = GLOBAL_MATRIX.get();
1259        char      **names = ARB_calloc<char*>(matr->nentries+2);
1260
1261        for (size_t i=0; i<matr->nentries; i++) {
1262            names[i] = matr->entries[i]->name;
1263        }
1264        di_assert(matr->nentries == matr->matrix->size());
1265        tree = neighbourjoining(names, *matr->matrix, new SimpleRoot);
1266
1267        if (bootstrap_flag) {
1268            error = ctree->insert_tree_weighted(tree, matr->nentries, 1, false);
1269            UNCOVERED();
1270            destroy(tree); tree = NULp;
1271            loop_count++;
1272            progress->inc();
1273            if (!bootstrap_count) { // when waiting for kill
1274                int        t     = time(NULp);
1275                static int tlast = 0;
1276
1277                if (t>tlast) {
1278                    progress->force_update();
1279                    tlast = t;
1280                }
1281            }
1282        }
1283        free(names);
1284    } while (bootstrap_flag && loop_count != bootstrap_count);
1285
1286    if (!error) {
1287        if (bootstrap_flag) {
1288            tree = ctree->get_consensus_tree(error);
1289            progress->inc();
1290            if (!error) {
1291                error = GBT_is_invalid(tree);
1292                di_assert(!error);
1293            }
1294        }
1295
1296        if (!error) {
1297            char *tree_name = aw_root->awar(AWAR_DIST_TREE_STD_NAME)->read_string();
1298            GB_begin_transaction(GLOBAL_gb_main);
1299            error = GBT_write_tree(GLOBAL_gb_main, tree_name, tree);
1300
1301            if (!error) {
1302                char *filter_name = AWT_get_combined_filter_name(aw_root, "dist");
1303                int   transr      = aw_root->awar(AWAR_DIST_CORR_TRANS)->read_int();
1304
1305                const char *comment;
1306                if (enum_trans_to_string[transr]) {
1307                    comment = GBS_global_string("PRG=dnadist CORR=%s FILTER=%s PKG=ARB", enum_trans_to_string[transr], filter_name);
1308                }
1309                else {
1310                    di_assert(transr == DI_TRANSFORMATION_FROM_TREE);
1311                    const char *treename = aw_root->awar(AWAR_DIST_TREE_CURR_NAME)->read_char_pntr();
1312                    comment = GBS_global_string("PRG=treedist (from '%s') PKG=ARB", treename);
1313                }
1314
1315                error = GBT_write_tree_remark(GLOBAL_gb_main, tree_name, comment);
1316                free(filter_name);
1317            }
1318            error = GB_end_transaction(GLOBAL_gb_main, error);
1319            free(tree_name);
1320        }
1321    }
1322
1323    UNCOVERED();
1324    destroy(tree);
1325
1326    // aw_status(); // remove 'abort' flag (@@@ got no equiv for arb_progress yet. really needed?)
1327
1328    if (bootstrap_flag) {
1329        if (all_names) delete all_names;
1330        GLOBAL_MATRIX.forget();
1331    }
1332#if defined(DEBUG)
1333    else {
1334        di_assert(!all_names);
1335    }
1336#endif // DEBUG
1337
1338    progress->done();
1339    if (error) {
1340        aw_message(error);
1341    }
1342    else {
1343        need_recalc.tree = false;
1344        aw_root->awar(AWAR_TREE_REFRESH)->touch();
1345    }
1346}
1347
1348
1349static void di_autodetect_callback(AW_window *aww) {
1350    GB_push_transaction(GLOBAL_gb_main);
1351
1352    GLOBAL_MATRIX.forget();
1353
1354    AW_root  *aw_root = aww->get_root();
1355    char     *use     = aw_root->awar(AWAR_DIST_ALIGNMENT)->read_string();
1356    long      ali_len = GBT_get_alignment_len(GLOBAL_gb_main, use);
1357    GB_ERROR  error   = NULp;
1358
1359    if (ali_len<=0) {
1360        GB_pop_transaction(GLOBAL_gb_main);
1361        error = "Please select a valid alignment";
1362        GB_clear_error();
1363    }
1364    else {
1365        arb_progress progress("Analyzing data");
1366
1367        char *filter_str = aw_root->awar(AWAR_DIST_FILTER_FILTER)->read_string();
1368        // char *cancel     = aw_root->awar(AWAR_DIST_CANCEL_CHARS)->read_string();
1369
1370        AliView *aliview = NULp;
1371        {
1372            AP_filter *ap_filter = NULp;
1373            long       flen  = strlen(filter_str);
1374
1375            if (flen == ali_len) {
1376                ap_filter = new AP_filter(filter_str, "0", ali_len);
1377            }
1378            else {
1379                if (flen) {
1380                    aw_message("Warning: your filter len is not equal to the alignment len\nfilter got truncated with zeros or cutted");
1381                    ap_filter = new AP_filter(filter_str, "0", ali_len);
1382                }
1383                else {
1384                    ap_filter = new AP_filter(ali_len); // unfiltered
1385                }
1386            }
1387
1388            error = ap_filter->is_invalid();
1389            if (!error) {
1390                AP_weights ap_weights(ap_filter);
1391                aliview = new AliView(GLOBAL_gb_main, *ap_filter, ap_weights, use);
1392            }
1393            delete ap_filter;
1394        }
1395
1396        if (error) {
1397            GB_pop_transaction(GLOBAL_gb_main);
1398        }
1399        else {
1400            DI_MATRIX phm(*aliview);
1401
1402            {
1403                char *load_what      = aw_root->awar(AWAR_DIST_WHICH_SPECIES)->read_string();
1404                char *sort_tree_name = aw_root->awar(AWAR_DIST_TREE_SORT_NAME)->read_string();
1405
1406                LoadWhat all_flag = (strcmp(load_what, "all") == 0) ? DI_LOAD_ALL : DI_LOAD_MARKED;
1407
1408                GLOBAL_MATRIX.forget();
1409
1410                MatrixOrder order(GLOBAL_gb_main, sort_tree_name);
1411                error = phm.load(all_flag, order, true, NULp);
1412
1413                free(sort_tree_name);
1414                free(load_what);
1415            }
1416
1417            GB_pop_transaction(GLOBAL_gb_main);
1418
1419            if (!error) {
1420                progress.subtitle("Search Correction");
1421
1422                string msg;
1423                DI_TRANSFORMATION detected = phm.detect_transformation(msg);
1424                aw_root->awar(AWAR_DIST_CORR_TRANS)->write_int(detected);
1425                aw_message(msg.c_str());
1426            }
1427        }
1428
1429        // free(cancel);
1430        delete aliview;
1431
1432        free(filter_str);
1433    }
1434
1435    if (error) aw_message(error);
1436
1437    free(use);
1438}
1439
1440__ATTR__NORETURN static void di_exit(AW_window *aww) {
1441    if (GLOBAL_gb_main) {
1442        AW_root *aw_root = aww->get_root();
1443        shutdown_macro_recording(aw_root);
1444        aw_root->unlink_awars_from_DB(GLOBAL_gb_main);
1445        GB_close(GLOBAL_gb_main);
1446    }
1447    GLOBAL_MATRIX.set_changed_cb(NULp);
1448    exit(EXIT_SUCCESS);
1449}
1450
1451static void di_calculate_full_matrix_cb(AW_window *aww, const WeightedFilter *weighted_filter) {
1452    recalculate_matrix_cb = new BoundWindowCallback(aww, makeWindowCallback(di_calculate_full_matrix_cb, weighted_filter));
1453
1454    GLOBAL_MATRIX.forget_if_not_has_type(DI_MATRIX_FULL);
1455    GB_ERROR error = di_calculate_matrix(aww->get_root(), weighted_filter, 0, true, NULp);
1456    aw_message_if(error);
1457    last_matrix_calculation_error = error;
1458    if (!error) tree_needs_recalc_cb();
1459}
1460
1461static void di_calculate_compressed_matrix_cb(AW_window *aww, WeightedFilter *weighted_filter) {
1462    recalculate_matrix_cb = new BoundWindowCallback(aww, makeWindowCallback(di_calculate_compressed_matrix_cb, weighted_filter));
1463
1464    GB_transaction ta(GLOBAL_gb_main);
1465
1466    AW_root  *aw_root  = aww->get_root();
1467    char     *treename = aw_root->awar(AWAR_DIST_TREE_COMP_NAME)->read_string();
1468    GB_ERROR  error    = NULp;
1469    TreeNode  *tree    = GBT_read_tree(GLOBAL_gb_main, treename, new SimpleRoot);
1470
1471    if (!tree) {
1472        error = GB_await_error();
1473    }
1474    else {
1475        {
1476            LocallyModify<MatrixDisplay*> skipRefresh(matrixDisplay, NULp); // skip refresh, until matrix has been compressed
1477
1478            GLOBAL_MATRIX.forget(); // always forget (as tree might have changed)
1479            error = di_calculate_matrix(aw_root, weighted_filter, 0, true, NULp);
1480            if (!error && !GLOBAL_MATRIX.exists()) {
1481                error = "Failed to calculate your matrix (bug?)";
1482            }
1483            if (!error) {
1484                error = GLOBAL_MATRIX.get()->compress(tree);
1485            }
1486        }
1487        UNCOVERED();
1488        destroy(tree);
1489
1490        // now force refresh
1491        if (matrixDisplay) {
1492            matrixDisplay->mark(MatrixDisplay::NEED_SETUP);
1493            matrixDisplay->update_display();
1494        }
1495    }
1496    free(treename);
1497    aw_message_if(error);
1498    last_matrix_calculation_error = error;
1499    if (!error) tree_needs_recalc_cb();
1500}
1501
1502static void di_define_sort_tree_name_cb(AW_window *aww) {
1503    AW_root *aw_root = aww->get_root();
1504    char *tree_name = aw_root->awar(AWAR_DIST_TREE_CURR_NAME)->read_string();
1505    aw_root->awar(AWAR_DIST_TREE_SORT_NAME)->write_string(tree_name);
1506    free(tree_name);
1507}
1508static void di_define_compression_tree_name_cb(AW_window *aww) {
1509    AW_root *aw_root = aww->get_root();
1510    char *tree_name = aw_root->awar(AWAR_DIST_TREE_CURR_NAME)->read_string();
1511    aw_root->awar(AWAR_DIST_TREE_COMP_NAME)->write_string(tree_name);
1512    free(tree_name);
1513}
1514
1515static void di_define_save_tree_name_cb(AW_window *aww) {
1516    AW_root *aw_root = aww->get_root();
1517    char *tree_name = aw_root->awar(AWAR_DIST_TREE_CURR_NAME)->read_string();
1518    aw_root->awar(AWAR_DIST_TREE_STD_NAME)->write_string(tree_name);
1519    free(tree_name);
1520}
1521
1522
1523AW_window *DI_create_matrix_window(AW_root *aw_root) {
1524    AW_window_simple_menu *aws = new AW_window_simple_menu;
1525    aws->init(aw_root, "NEIGHBOUR JOINING", "NEIGHBOUR JOINING [ARB_DIST]");
1526    aws->load_xfig("di_ge_ma.fig");
1527    aws->button_length(10);
1528
1529    aws->at("close");
1530    aws->callback(di_exit);
1531    aws->create_button("CLOSE", "CLOSE", "C");
1532
1533    aws->at("help");
1534    aws->callback(makeHelpCallback("dist.hlp"));
1535    aws->create_button("HELP", "HELP", "H");
1536
1537
1538    GB_push_transaction(GLOBAL_gb_main);
1539
1540#if defined(DEBUG)
1541    AWT_create_debug_menu(aws);
1542#endif // DEBUG
1543
1544    aws->create_menu("File", "F", AWM_ALL);
1545    insert_macro_menu_entry(aws, false);
1546    aws->insert_menu_topic("quit", "Quit", "Q", "quit.hlp", AWM_ALL, di_exit);
1547
1548    aws->create_menu("Properties", "P", AWM_ALL);
1549    aws->insert_menu_topic("frame_props", "Frame settings ...", "F", "props_frame.hlp", AWM_ALL, AW_preset_window);
1550    aws->sep______________();
1551    AW_insert_common_property_menu_entries(aws);
1552    aws->sep______________();
1553    aws->insert_menu_topic("save_props", "Save Properties (dist.arb)", "S", "savedef.hlp", AWM_ALL, AW_save_properties);
1554
1555    aws->insert_help_topic("ARB_DIST help", "D", "dist.hlp", AWM_ALL, makeHelpCallback("dist.hlp"));
1556
1557    // ------------------
1558    //      left side
1559
1560    aws->at("which_species");
1561    aws->create_option_menu(AWAR_DIST_WHICH_SPECIES, true);
1562    aws->insert_option("all", "a", "all");
1563    aws->insert_default_option("marked",   "m", "marked");
1564    aws->update_option_menu();
1565
1566    aws->at("which_alignment");
1567    awt_create_ALI_selection_list(GLOBAL_gb_main, (AW_window *)aws, AWAR_DIST_ALIGNMENT, "*=");
1568
1569    // filter & weights
1570
1571    AW_awar *awar_dist_alignment    = aws->get_root()->awar_string(AWAR_DIST_ALIGNMENT);
1572    WeightedFilter *weighted_filter = // do NOT free (bound to callbacks)
1573        new WeightedFilter(GLOBAL_gb_main, aws->get_root(), AWAR_DIST_FILTER_NAME, AWAR_DIST_COLUMN_STAT_NAME, awar_dist_alignment);
1574
1575    aws->at("filter_select");
1576    aws->callback(makeCreateWindowCallback(awt_create_select_filter_win, weighted_filter->get_adfiltercbstruct()));
1577    aws->create_button("SELECT_FILTER", AWAR_DIST_FILTER_NAME);
1578
1579    aws->at("weights_select");
1580    aws->sens_mask(AWM_EXP);
1581    aws->callback(makeCreateWindowCallback(COLSTAT_create_selection_window, weighted_filter->get_column_stat()));
1582    aws->create_button("SELECT_COL_STAT", AWAR_DIST_COLUMN_STAT_NAME);
1583    aws->sens_mask(AWM_ALL);
1584
1585    aws->at("which_cancel");
1586    aws->create_input_field(AWAR_DIST_CANCEL_CHARS, 12);
1587
1588    aws->at("cancel_select");
1589    aws->callback(awt_create_select_cancel_window);
1590    aws->create_button("SELECT_CANCEL_CHARS", "Info", "C");
1591
1592    aws->at("change_matrix");
1593    aws->callback(create_dna_matrix_window);
1594    aws->create_button("EDIT_MATRIX", "Edit Matrix");
1595
1596    aws->at("enable");
1597    aws->create_toggle(AWAR_DIST_MATRIX_DNA_ENABLED);
1598
1599    aws->at("which_correction");
1600    aws->create_option_menu(AWAR_DIST_CORR_TRANS, true);
1601    aws->insert_option("none",                    "n", (int)DI_TRANSFORMATION_NONE);
1602    aws->insert_option("similarity",              "n", (int)DI_TRANSFORMATION_SIMILARITY);
1603    aws->insert_option("jukes-cantor (dna)",      "c", (int)DI_TRANSFORMATION_JUKES_CANTOR);
1604    aws->insert_option("felsenstein (dna)",       "f", (int)DI_TRANSFORMATION_FELSENSTEIN);
1605    aws->insert_option("olsen (dna)",             "o", (int)DI_TRANSFORMATION_OLSEN);
1606    aws->insert_option("felsenstein/voigt (exp)", "1", (int)DI_TRANSFORMATION_FELSENSTEIN_VOIGT);
1607    aws->insert_option("olsen/voigt (exp)",       "2", (int)DI_TRANSFORMATION_OLSEN_VOIGT);
1608    aws->insert_option("kimura (pro)",            "k", (int)DI_TRANSFORMATION_KIMURA);
1609    aws->insert_option("PAM (protein)",           "c", (int)DI_TRANSFORMATION_PAM);
1610    aws->insert_option("Cat. Hall(exp)",          "c", (int)DI_TRANSFORMATION_CATEGORIES_HALL);
1611    aws->insert_option("Cat. Barker(exp)",        "c", (int)DI_TRANSFORMATION_CATEGORIES_BARKER);
1612    aws->insert_option("Cat.Chem (exp)",          "c", (int)DI_TRANSFORMATION_CATEGORIES_CHEMICAL);
1613    aws->insert_option("from selected tree",      "t", (int)DI_TRANSFORMATION_FROM_TREE);
1614    aws->insert_default_option("unknown",         "u", (int)DI_TRANSFORMATION_NONE);
1615
1616    aws->update_option_menu();
1617
1618    aws->at("autodetect");   // auto
1619    aws->callback(di_autodetect_callback);
1620    aws->sens_mask(AWM_EXP);
1621    aws->create_button("AUTODETECT_CORRECTION", "AUTODETECT", "A");
1622    aws->sens_mask(AWM_ALL);
1623
1624    // -------------------
1625    //      right side
1626
1627
1628    aws->at("mark_distance");
1629    aws->callback(makeWindowCallback(di_mark_by_distance, weighted_filter));
1630    aws->create_autosize_button("MARK_BY_DIST", "Mark all species");
1631
1632    aws->at("mark_lower");
1633    aws->create_input_field(AWAR_DIST_MIN_DIST, 5);
1634
1635    aws->at("mark_upper");
1636    aws->create_input_field(AWAR_DIST_MAX_DIST, 5);
1637
1638    // -----------------
1639
1640    // tree selection
1641
1642    aws->at("tree_list");
1643    awt_create_TREE_selection_list(GLOBAL_gb_main, aws, AWAR_DIST_TREE_CURR_NAME, true);
1644
1645    aws->at("detect_clusters");
1646    aws->callback(makeCreateWindowCallback(DI_create_cluster_detection_window, weighted_filter));
1647    aws->create_autosize_button("DETECT_CLUSTERS", "Detect homogenous clusters in tree", "D");
1648
1649    // matrix calculation
1650
1651    aws->button_length(18);
1652
1653    aws->at("calculate");
1654    aws->callback(makeWindowCallback(di_calculate_full_matrix_cb, weighted_filter));
1655    aws->create_button("CALC_FULL_MATRIX", "Calculate\nFull Matrix", "F");
1656
1657    aws->at("compress");
1658    aws->callback(makeWindowCallback(di_calculate_compressed_matrix_cb, weighted_filter));
1659    aws->create_button("CALC_COMPRESSED_MATRIX", "Calculate\nCompressed Matrix", "C");
1660
1661    recalculate_matrix_cb = new BoundWindowCallback(aws, makeWindowCallback(di_calculate_full_matrix_cb, weighted_filter));
1662
1663    aws->button_length(13);
1664
1665    {
1666        static save_matrix_params sparams;
1667
1668        sparams.awar_base       = AWAR_DIST_SAVE_MATRIX_BASE;
1669        sparams.weighted_filter = weighted_filter;
1670
1671        aws->at("save_matrix");
1672        aws->callback(makeCreateWindowCallback(DI_create_save_matrix_window, &sparams));
1673        aws->create_button("SAVE_MATRIX", "Save matrix", "M");
1674
1675        aws->at("view_matrix");
1676        aws->callback(makeWindowCallback(di_view_matrix_cb, &sparams));
1677        aws->create_button("VIEW_MATRIX", "View matrix", "V");
1678    }
1679
1680    aws->button_length(22);
1681    aws->at("use_compr_tree");
1682    aws->callback(di_define_compression_tree_name_cb);
1683    aws->create_button("USE_COMPRESSION_TREE", "Use to compress", "");
1684    aws->at("use_sort_tree");
1685    aws->callback(di_define_sort_tree_name_cb);
1686    aws->create_button("USE_SORT_TREE", "Use to sort", "");
1687
1688    aws->at("compr_tree_name"); aws->create_input_field(AWAR_DIST_TREE_COMP_NAME, 12);
1689    aws->at("sort_tree_name");  aws->create_input_field(AWAR_DIST_TREE_SORT_NAME, 12);
1690
1691    // tree calculation
1692
1693    aws->button_length(18);
1694
1695    aws->at("t_calculate");
1696    aws->callback(makeWindowCallback(di_calculate_tree_cb, weighted_filter, false));
1697    aws->create_button("CALC_TREE", "Calculate \ntree", "C");
1698
1699    aws->at("bootstrap");
1700    aws->callback(makeWindowCallback(di_calculate_tree_cb, weighted_filter, true));
1701    aws->create_button("CALC_BOOTSTRAP_TREE", "Calculate \nbootstrap tree");
1702
1703    recalculate_tree_cb = new BoundWindowCallback(aws, makeWindowCallback(di_calculate_tree_cb, weighted_filter, false));
1704
1705    aws->button_length(22);
1706    aws->at("use_existing");
1707    aws->callback(di_define_save_tree_name_cb);
1708    aws->create_button("USE_NAME", "Use as new tree name", "");
1709
1710    aws->at("calc_tree_name");
1711    aws->create_input_field(AWAR_DIST_TREE_STD_NAME, 12);
1712
1713    aws->at("bcount");
1714    aws->create_input_field(AWAR_DIST_BOOTSTRAP_COUNT, 7);
1715
1716    {
1717        aws->sens_mask(AWM_EXP);
1718
1719        aws->at("auto_calc_tree");
1720        aws->label("Auto calculate tree");
1721        aws->create_toggle(AWAR_DIST_MATRIX_AUTO_CALC_TREE);
1722
1723        aws->at("auto_recalc");
1724        aws->label("Auto recalculate");
1725        aws->create_toggle(AWAR_DIST_MATRIX_AUTO_RECALC);
1726
1727        aws->sens_mask(AWM_ALL);
1728    }
1729
1730    bool disable_autocalc = !ARB_in_expert_mode(aw_root);
1731    if (disable_autocalc) {
1732        aw_root->awar(AWAR_DIST_MATRIX_AUTO_RECALC)->write_int(0);
1733        aw_root->awar(AWAR_DIST_MATRIX_AUTO_CALC_TREE)->write_int(0);
1734    }
1735
1736    GB_pop_transaction(GLOBAL_gb_main);
1737    return aws;
1738}
1739
1740// --------------------------------------------------------------------------------
1741
1742#ifdef UNIT_TESTS
1743#include <arb_diff.h>
1744#include <arb_file.h>
1745
1746#ifndef TEST_UNIT_H
1747#include <test_unit.h>
1748#endif
1749
1750class DIST_testenv : virtual Noncopyable {
1751    GB_shell  shell;
1752    GBDATA   *gb_main;
1753    AliView  *ali_view;
1754
1755public:
1756    DIST_testenv(const char *dbname, const char *aliName)
1757        : ali_view(NULp)
1758    {
1759        gb_main = GB_open(dbname, "r");
1760        TEST_REJECT_NULL(gb_main);
1761
1762        GB_transaction ta(gb_main);
1763        size_t         aliLength = GBT_get_alignment_len(gb_main, aliName);
1764
1765        AP_filter filter(aliLength);
1766        if (!filter.is_invalid()) {
1767            AP_weights weights(&filter);
1768            ali_view = new AliView(gb_main, filter, weights, aliName);
1769        }
1770    }
1771    ~DIST_testenv() {
1772        delete ali_view;
1773        GB_close(gb_main);
1774    }
1775
1776    const AliView& aliview() const { return *ali_view; }
1777    GBDATA *gbmain() const { return gb_main; }
1778};
1779
1780void TEST_matrix() {
1781    for (int iat = GB_AT_RNA; iat<=GB_AT_AA; ++iat) {
1782        GB_alignment_type at = GB_alignment_type(iat);
1783        // ---------------
1784        //      setup
1785        //                               GB_AT_RNA         GB_AT_DNA           GB_AT_AA
1786        const char *db_name[]=  { NULp, "TEST_trees.arb", "TEST_realign.arb", "TEST_realign.arb", };
1787        const char *ali_name[]= { NULp, "ali_5s",         "ali_dna",          "ali_pro",          };
1788
1789        TEST_ANNOTATE(GBS_global_string("ali_name=%s", ali_name[at]));
1790        DIST_testenv env(db_name[at], ali_name[at]);
1791
1792        DI_MATRIX matrix(env.aliview());
1793        MatrixOrder order(env.gbmain(), "tree_abc"); // no such tree!
1794        TEST_EXPECT_NO_ERROR(matrix.load(DI_LOAD_MARKED, order, true, NULp));
1795
1796        // -------------------------------
1797        //      detect_transformation
1798        DI_TRANSFORMATION detected_trans;
1799        {
1800            string msg;
1801
1802            detected_trans = matrix.detect_transformation(msg);
1803            DI_TRANSFORMATION expected = DI_TRANSFORMATION_NONE_DETECTED;
1804            switch (at) {
1805                case GB_AT_RNA: expected = DI_TRANSFORMATION_NONE;         break;
1806                case GB_AT_DNA: expected = DI_TRANSFORMATION_JUKES_CANTOR; break;
1807                case GB_AT_AA:  expected = DI_TRANSFORMATION_PAM;          break;
1808                case GB_AT_UNKNOWN: di_assert(0); break;
1809            }
1810            TEST_EXPECT_EQUAL(detected_trans, expected);
1811        }
1812
1813        // ------------------------------
1814        //      calculate the matrix
1815
1816        // @@@ does not test user-defined transformation-matrix!
1817        if (at == GB_AT_AA) {
1818            matrix.calculate_pro(detected_trans, NULp);
1819        }
1820        else {
1821            if (at == GB_AT_RNA) detected_trans = DI_TRANSFORMATION_FELSENSTEIN; // force calculate_overall_freqs
1822            matrix.calculate("", detected_trans, NULp, NULp);
1823        }
1824
1825        // -----------------------------------
1826        //      save in available formats
1827
1828        for (DI_SAVE_TYPE saveType = DI_SAVE_PHYLIP_COMP; saveType<=DI_SAVE_TABBED; saveType = DI_SAVE_TYPE(saveType+1)) {
1829            const char *savename = "distance/matrix.out";
1830            matrix.save(savename, saveType);
1831
1832            const char *suffixAT[] = { NULp, "rna", "dna", "pro" };
1833            const char *suffixST[] = { "phylipComp", "readable", "tabbed" };
1834            char *expected = GBS_global_string_copy("distance/matrix.%s.%s.expected", suffixAT[at], suffixST[saveType]);
1835
1836// #define TEST_AUTO_UPDATE // uncomment to auto-update expected matrices
1837
1838#if defined(TEST_AUTO_UPDATE)
1839            TEST_COPY_FILE(savename, expected);
1840#else
1841            TEST_EXPECT_TEXTFILES_EQUAL(savename, expected);
1842#endif // TEST_AUTO_UPDATE
1843            TEST_EXPECT_ZERO_OR_SHOW_ERRNO(GB_unlink(savename));
1844
1845            free(expected);
1846        }
1847    }
1848}
1849
1850#endif // UNIT_TESTS
1851
1852// --------------------------------------------------------------------------------
1853
Note: See TracBrowser for help on using the repository browser.