source: trunk/GDE/SINA/builddir/src/cseq.cpp

Last change on this file was 19170, checked in by westram, 2 years ago
  • sina source
    • unpack + remove tarball
    • no longer ignore sina builddir.
File size: 21.0 KB
Line 
1/*
2Copyright (c) 2006-2018 Elmar Pruesse <elmar.pruesse@ucdenver.edu>
3
4This file is part of SINA.
5SINA is free software: you can redistribute it and/or modify it under
6the terms of the GNU General Public License as published by the Free
7Software Foundation, either version 3 of the License, or (at your
8option) any later version.
9
10SINA is distributed in the hope that it will be useful, but WITHOUT ANY
11WARRANTY; without even the implied warranty of MERCHANTABILITY or
12FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13for more details.
14
15You should have received a copy of the GNU General Public License
16along with SINA.  If not, see <http://www.gnu.org/licenses/>.
17
18Additional permission under GNU GPL version 3 section 7
19
20If you modify SINA, or any covered work, by linking or combining it
21with components of ARB (or a modified version of that software),
22containing parts covered by the terms of the
23ARB-public-library-license, the licensors of SINA grant you additional
24permission to convey the resulting work. Corresponding Source for a
25non-source form of such a combination shall include the source code
26for the parts of ARB used as well as that of the covered work.
27*/
28
29#include "cseq.h"
30#include "log.h"
31#include <iostream>
32#include <iomanip>
33#include <algorithm>
34#include <sstream>
35#include <string>
36#include <vector>
37#include <list>
38#include <map>
39#include <functional>
40#include <cmath>
41#include <zlib.h>
42
43using namespace std;
44using namespace sina;
45
46static auto logger = Log::create_logger("cseq");
47
48cseq_base::cseq_base(const char *_name, const char *_data)
49    : name(_name)
50{
51    if (_data != nullptr) {
52        append(_data);
53    }
54}
55
56 
57void
58cseq_base::clearSequence() {
59    bases.clear(); 
60    alignment_width = 0; 
61}
62
63cseq_base&
64cseq_base::append(const char *str) {
65    //FIXME: keep internal '.'s
66    while(*str != 0) {
67        if (*str != ' ' && *str != '\t' && *str != '\n' &&  *str != '\r') {
68            if (*str != '-' && *str != '.') {
69                bases.emplace_back(alignment_width, *str);
70            }
71            alignment_width++;
72        }
73        str++;
74    }
75
76    return *this;
77}
78
79cseq_base&
80cseq_base::append(const aligned_base& ab) {
81    if (ab.getPosition() >= alignment_width) {
82        // it's allowed to have more than one base on the
83        // same position. we fix them in one go later on.
84        bases.push_back(ab);
85        alignment_width = ab.getPosition();
86    } else {
87        // but the new base must not come _before_ the last one
88        logger->error("$ cseq::append(): wrong order! {}({}<{})",
89                      ab.getBase(), ab.getPosition(), alignment_width);
90
91        bases.emplace_back(alignment_width, ab.getBase());
92    }
93
94    return *this;
95}
96
97
98void
99cseq_base::setWidth(vidx_type newWidth) {
100    if (bases.empty() || newWidth >= bases.back().getPosition() + 1) {
101        // modify at will if changing only number of trailing gaps
102        alignment_width = newWidth;
103        return;
104    }
105
106    // we can't shrink to less than 0 gaps
107    if (newWidth < size()) {
108        logger->critical("Cannot shrink '{}' aligment width to {} - got {} bases",
109                         getName(), newWidth, size());
110        throw std::runtime_error(
111                "Attempted to shrink alignment width below base count"
112            );
113    }
114
115    // find the number of bases from the right where
116    // <position-of-base> + <number-of-following-bases>
117    // is at most <width-of-alignment>
118    unsigned int skip;
119    for(skip = 0; skip < size(); skip++) {
120        if (bases[size() - skip - 1].getPosition() + skip < newWidth) {
121            break;
122        }
123    }
124
125    for (unsigned int i = skip; i > 0; --i) {
126        bases[size() - i].setPosition(newWidth - i);
127    }
128    alignment_width = newWidth;
129
130    logger->warn("moved last {} bases to shrink alignment to {} columns",
131                 skip, alignment_width);
132}
133
134
135string
136cseq_base::getAligned(bool nodots, bool dna) const {
137    string aligned;
138    aligned.reserve(alignment_width);
139
140    char dot='.';
141    if (nodots) {
142        dot='-';
143    }
144
145    auto it = bases.begin(), it_end = bases.end();
146
147    unsigned int cursor = 0;
148    for (; it != it_end; ++it) {
149        unsigned int pos = it->getPosition();
150
151        // advance cursor by filling with gap character
152        aligned.append(pos - cursor, dot);
153        // (only the first "gap" is dots)
154        dot = '-';
155        cursor = pos;
156
157        // print base
158        if (dna) {
159            aligned.append(1, it->getBase().iupac_dna());
160        } else {
161            aligned.append(1, it->getBase().iupac_rna());
162        }
163        cursor++;
164    }
165
166    if (cursor < alignment_width) {
167        if (!nodots) {
168            dot='.';
169        }
170        aligned.append(alignment_width - cursor, dot);
171    }
172
173    return aligned;
174}
175
176string
177cseq_base::getBases() const {
178    string basestr;
179    basestr.reserve(bases.size());
180
181    for (auto base : bases) {
182        basestr += base.getBase();
183    }
184
185    return basestr;
186}
187
188struct compressed_data {
189    char id;
190    unsigned short size;
191    unsigned char data[];
192};
193
194void
195cseq_base::compressAligned(std::vector<unsigned char> &out) {
196    vector<unsigned char> buf;
197    using uint = unsigned int;
198
199    bases.emplace_back(alignment_width);
200    const uint bas = bases.size();
201
202    const uint orig_size = 8 * bas;
203    buf.resize(orig_size);
204
205    for (uint i=0; i<bas; ++i) {
206        buf[i]=bases[i].getBase();
207    }
208
209    idx_type last=0;
210    for (uint i=0; i < bas; ++i) {
211        idx_type idx = bases[i].getPosition();
212        idx_type diff = idx - last;
213        for (uint j = 0; j < sizeof(idx_type); ++j) {
214            buf[(j+1)*bas+i]  = (unsigned char)(diff &0xFF);
215            diff >>=8;
216        }
217        last = idx;
218    }
219
220    unsigned long compr_size = compressBound(orig_size);
221    out.resize(compr_size);
222    auto *cd = reinterpret_cast<compressed_data*>(&out.front());
223
224    cd->id='#';
225    cd->size=(unsigned short)orig_size;
226
227    compress2(cd->data, &compr_size, &buf.front(), orig_size,9);
228
229    out.resize(compr_size+sizeof(compressed_data));
230}
231
232void
233cseq_base::assignFromCompressed(const void* data, size_t len) {
234    vector<unsigned char> buf;
235    using uint = unsigned int;
236    const auto *cd = reinterpret_cast<const compressed_data*>(data);
237    buf.resize(cd->size);
238
239    unsigned long compr_size = len - sizeof(compressed_data);
240    unsigned long orig_size = cd->size;
241
242    uncompress(&buf.front(), &orig_size, cd->data, compr_size);
243
244    const uint bas = orig_size / 8;
245
246    bases.clear();
247    bases.reserve(bas);
248
249
250    idx_type last = 0;
251    for (uint i=0; i<bas; ++i) {
252        idx_type diff = 0;
253
254        for (int j = sizeof(idx_type); j != -1; --j) {
255            diff <<= 8;
256            diff |= buf[(j+1)*bas+i];
257        }
258        last+=diff;
259        bases.emplace_back(last, buf[i]);
260    }
261    alignment_width = bases.back().getPosition();
262    bases.pop_back();
263}
264
265
266
267char
268cseq_base::operator[](cseq_base::vidx_type i) const {
269    vector<aligned_base>::const_iterator it = getIterator(i);
270    if (it != bases.end() && i == it->getPosition()) {
271        return it->getBase();
272    }
273    return '-';
274}
275
276
277std::ostream&
278sina::operator<<(std::ostream& out, const cseq_base& c) {
279    out << c.getName();
280    return out;
281}
282
283void
284cseq_base::reverse() {
285    std::reverse(bases.begin(), bases.end());
286    for (auto& base : bases) {
287        base.setPosition(alignment_width - 1 - base.getPosition());
288    }
289}
290
291void
292cseq_base::complement() {
293    for (auto& base : bases) {
294        base.complement();
295    }
296}
297
298void
299cseq_base::upperCaseAll() {
300    for (auto& base : bases) {
301        base.setUpperCase();
302    }
303}
304
305
306template<typename RandIt, typename StrictWeakOrdering>
307RandIt group_by(RandIt& a, RandIt& b,
308                StrictWeakOrdering & /*cmp*/) {
309    sort(a,b);
310    RandIt end = unique(a,b);
311    return end;
312}
313
314template<typename RandIt>
315typename RandIt::value_type group_by(RandIt& a, RandIt& b) {
316    return group_by(a,b, less<typename RandIt::value_type>());
317}
318
319string color_code(const string& in) {
320    string::const_iterator in_end = in.end();
321    stringstream tmp;
322    bool colored = false;
323    for (string::const_iterator it = in.begin(); it != in_end; ++it) {
324        switch(*it) {
325                case 'a':
326                case 'A':
327                    tmp << "\033[34m";
328                    colored = true;
329                    break;
330                case 'g':
331                case 'G':
332                    tmp << "\033[35m";
333                    colored = true;
334                    break;
335                case 'c':
336                case 'C':
337                    tmp << "\033[32m";
338                    colored = true;
339                    break;
340                case 't':
341                case 'T':
342                case 'u':
343                case 'U':
344                    tmp << "\033[33m";
345                    colored = true;
346                    break;
347                default:
348                    if (colored) {
349                        tmp << "\033[0m";
350                        colored = false;
351                    }
352        }
353        tmp << *it;
354    }
355    if (colored) {
356        tmp << "\033[0m";
357    }
358    return tmp.str();
359}
360
361void
362cseq_base::write_alignment(std::ostream& ofs, std::vector<const cseq_base*>& seqs,
363                      cseq_base::idx_type from_pos,
364                      cseq_base::idx_type to_pos,
365                      bool colors
366                      ) {
367    if (seqs.empty()) {
368        ofs << "cseq::write_alignment(): no sequences?" << endl;
369        return;
370    }
371    if (from_pos > to_pos || to_pos >= seqs[0]->getWidth()) {
372        ofs << "cseq::write_alignment(): range out of bounds!" << endl;
373        return;
374    }
375
376    vector<string> out(seqs.size());
377
378    char outchar[seqs.size()];
379    auto jmax = seqs.size();
380
381    for (auto i = from_pos; i <= to_pos; ++i) {
382        bool gap = true;
383        for (auto j = 0u; j < jmax; ++j) {
384            outchar[j] = (*seqs[j])[i];
385            if (outchar[j] != '-') {
386                gap = false;
387            }
388        }
389
390        if (!gap || i == to_pos-1 ) {
391            for (auto j = 0u; j < jmax; ++j) {
392                out[j].append(1, outchar[j]);
393            }
394        }
395    }
396
397    std::map<string,list<int> > mymap;
398
399    size_t maxlen = 0;
400    for (auto it = out.begin(); it != out.end(); ++it) {
401        maxlen = std::max(maxlen, it->size());
402        mymap[*it].push_back(it - out.begin());
403    }
404
405    ofs << "Dumping pos " << from_pos << " through " << to_pos << ":" << endl;
406    for (unsigned int i = 0; i < maxlen; i+=100) {
407        int len = maxlen - i;
408        len = std::min(100,len);
409        for (auto & it : mymap) {
410            if (colors) {
411                ofs << color_code(it.first.substr(i, len)) << " ";
412            } else {
413                ofs << it.first.substr(i, len) << " ";
414            }
415            bool range=false, is_last=false, is_secondlast=false;
416            int last=-2;
417            for (int jt : it.second) {
418                if (range) {
419                    if (jt != last+1) {
420                        ofs << last;
421                        range=false;
422                        ofs << " " << jt;
423                    }
424                } else {
425                    if (jt == last+1) {
426                        range=true;
427                        ofs << "-";
428                    } else {
429                        ofs << " " << jt;
430                    }
431                }
432                last = jt;
433                if (jt +1 == (int)seqs.size()) {
434                    is_last = true;
435                }
436                if (jt +2 == (int)seqs.size()) {
437                    is_secondlast = true;
438                }
439            }
440            if (range) {
441                ofs << last;
442            }
443            if (is_last) {
444                ofs << " <---(## NEW ##) ";
445            }
446            if (is_secondlast) {
447                ofs << " <---(%% ORIG %%) ";
448            }
449
450            ofs << endl;
451        }
452        ofs << endl;
453    }
454}
455
456void
457cseq_base::fix_duplicate_positions(std::ostream& log, bool lowercase, bool remove) {
458    idx_type total_inserts = 0;
459    idx_type longest_insert = 0;
460    idx_type orig_inserts = 0;
461
462    if (remove) {
463        log << "insertion=remove not implemented, using shift; ";
464    }
465
466    auto last_it = bases.begin();
467    auto bases_end = bases.end();
468    for (auto curr_it = bases.begin(); curr_it < bases_end; ++curr_it) {
469        // check for insertions
470        if (last_it->getPosition() == curr_it->getPosition()) {
471            // no move -> curr is insertion
472            if (curr_it+1 != bases_end) {
473                // not at end of sequence -> da capo
474                continue;
475            }
476            // curr is the last base.
477            // move iterator to end and fall through to placement
478            ++curr_it;
479        }
480        idx_type num_inserts = curr_it - last_it - 1;
481        if (num_inserts == 0) {
482            // no insertions. leave base untouched.
483            // remember last good position.
484            last_it = curr_it;
485            continue;
486        }
487
488        // WE HAVE AN INSERTION :)
489
490        // Determine the range where it may be placed:
491        // first free position
492        idx_type range_begin = last_it->getPosition() + 1; 
493        // first occupied/invalid position
494        idx_type range_end   = (curr_it == bases_end) ?
495            alignment_width : curr_it->getPosition();
496
497        // make last_it first base to be repositioned
498        ++last_it; 
499        // make curr_it last base to be repositioned
500        --curr_it;
501
502        orig_inserts = num_inserts;
503        if (range_end - range_begin < num_inserts) { // range too small
504            log << "shifting bases to fit in "<<num_inserts<<" bases at pos "<<range_begin<<" to "<<range_end<<";";
505            // last_it and curr_it are the bases enclosing our insertion
506            while (range_end - range_begin < num_inserts) {
507                int next_left_gap;
508                int next_right_gap;
509                auto left = last_it;
510                auto right = curr_it;
511               
512                // find first free gap to left of range
513                if (left == bases.begin()) {
514                    if (range_begin > 0) {
515                        next_left_gap = range_begin - 1;
516                    } else {
517                        next_left_gap = -1;
518                    }
519                } else {
520                    if ((left-1)->getPosition() + 1 < range_begin) {
521                        next_left_gap = range_begin - 1;
522                    } else {
523                        --left;
524                        while(left != bases.begin() && 
525                              (left-1)->getPosition() + 1 >= left->getPosition()) {
526                            --left;
527                        }
528                        next_left_gap = left->getPosition() - 1;
529                    }
530                }
531                   
532                // find first free gap to right of range
533
534                if (right + 1 == bases.end()) {
535                    if (range_end < alignment_width) {
536                        next_right_gap = range_end;
537                    } else {
538                        next_right_gap = -1;
539                    }
540                } else {
541                    if ((right+1)->getPosition() > range_end) {
542                        next_right_gap = range_end;
543                    } else {
544                        ++right;
545                        while(right+1 != bases.end() && 
546                              (right)->getPosition() +1 >= (right+1)->getPosition()) {
547                            ++right;
548                        }
549                        next_right_gap = right->getPosition() + 1;
550                    }
551                }
552
553
554                if (next_right_gap == -1 || 
555                    (next_left_gap != -1 && 
556                     range_begin - next_left_gap <= next_right_gap - (range_end - 1)) ) {
557                    if (next_left_gap == -1) {
558                        throw runtime_error("ERROR: no space to left and right?? "
559                                            "sequence longe than alignment?!");
560                    }
561                    num_inserts += last_it - left;
562                    range_begin = next_left_gap;
563                    last_it = left;
564                } else {
565                    num_inserts += right - curr_it;
566                    range_end = next_right_gap + 1;
567                    curr_it = right;
568                }
569
570            }
571        } else {
572            range_begin = range_end - num_inserts;
573        }
574        // make curr_it first base not to be positioned
575        ++curr_it;
576
577        for (; last_it != curr_it; ++last_it) {
578            last_it->setPosition(range_begin++);
579            if (lowercase) {
580                last_it->setLowerCase();
581            }
582        }
583
584        total_inserts+=num_inserts;
585        longest_insert = std::max(longest_insert,num_inserts);
586
587        last_it = curr_it;
588    }
589    if (total_inserts > 0) {
590        log << "total inserted bases=" << total_inserts << ";"
591            << "longest insertion=" << longest_insert << ";"
592            << "total inserted bases before shifting=" << orig_inserts << ";";
593    }
594}
595
596std::vector<std::pair<unsigned int, unsigned int>>
597cseq_base::find_differing_parts(const cseq_base& right) const {
598    using bases_it = std::vector<aligned_base>::const_iterator;
599    auto l_it = bases.begin(), l_end = bases.end();
600    auto r_it = right.bases.begin(), r_end = right.bases.end();
601   
602    std::vector<std::pair<unsigned int, unsigned int>> result;
603    int score = 0;
604    bool bad = false;
605    unsigned int start = 0;
606
607    int lpos = l_it->getPosition();
608    int rpos = r_it->getPosition();
609
610    while(l_it != l_end && r_it != r_end) {
611        if (lpos < rpos) {
612            score = 4;
613            ++l_it;
614        } else if ( rpos < lpos ) {
615            score = 4;
616            ++r_it;
617        } else { // rpos <=> lops
618            if ((char)l_it->getBase() != (char)r_it->getBase()) {
619                score = 4;
620            }
621            ++r_it;
622            ++l_it;
623        }
624        if (l_it != l_end) {
625            lpos = l_it->getPosition();
626        }
627        if (r_it != r_end) {
628            rpos = r_it->getPosition();
629        }
630        if (score > 0) {
631            if (!bad) {
632                int rpos = std::max(right.bases.begin(), r_it - 2)->getPosition();
633                start = std::min(lpos, rpos);
634                bad=true;
635            } else {
636                if (--score <= 0 && lpos == rpos) {
637                    result.push_back({start, lpos});
638                    bad=false;
639                }
640            }
641        }
642    }
643    if (bad) {
644        result.push_back({start, std::min(lpos, rpos)});
645    }
646   
647    return result;
648}
649
650
651float
652cseq_base::calcPairScore(const std::vector<int>& pairs) {
653    // create array to hold counts for base combinations
654    std::vector<int> count;
655    count.resize(65536);
656    for (int i=0; i<65536; ++i) {
657        count[i] = 0;
658    }
659
660    int num=0;
661
662    for(unsigned int i=0; i<pairs.size(); ++i) {
663        if (pairs[i] != 0) { // alignment position has "helix-parter"
664            unsigned char left, right;
665            left = operator[](i);
666            right = operator[](pairs[i]);
667
668            if ((left != '.' and right != '.')
669                and
670                (left != '-' or right != '-')) {
671                num++;
672                if (left < right) {
673                    count[((int)(left)<<8) + right]++;
674                } else {
675                    count[((int)(right)<<8) + left]++;
676                }
677            }
678        }
679    }
680
681#ifdef DEBUG
682    stringstream detail;
683    for (int i=0; i<65536; ++i) {
684       if (count[i]>0) {
685           detail << (char)(i>>8) << (char)(i&0xFF) << ": "
686                  << count[i]/2 << "  ";
687       }
688    }
689    if (detail.str() != "") {
690        logger->info("bp detail: {}", detail.str());
691    }
692#endif
693
694
695#if 1
696    float score =
697        count[((int)('-')<<8) + 'A'] * 0 +
698        count[((int)('-')<<8) + 'C'] * 0 +
699        count[((int)('-')<<8) + 'G'] * 0 +
700        count[((int)('-')<<8) + 'U'] * 0 +
701        count[((int)('A')<<8) + 'A'] * 0 +
702        count[((int)('A')<<8) + 'C'] * 0 +
703        count[((int)('A')<<8) + 'G'] * .5 +
704        count[((int)('A')<<8) + 'U'] * 1.1 +
705        count[((int)('C')<<8) + 'C'] * 0 +
706        count[((int)('C')<<8) + 'G'] * 1.5 +
707        count[((int)('C')<<8) + 'U'] * 0 +
708        count[((int)('G')<<8) + 'G'] * .4 +
709        count[((int)('G')<<8) + 'U'] * .9 +
710        count[((int)('U')<<8) + 'U'] * 0;
711
712#else
713  float score =
714        count[((int)('-')<<8) + 'A'] * -1 +
715        count[((int)('-')<<8) + 'C'] * -1 +
716        count[((int)('-')<<8) + 'G'] * -1 +
717        count[((int)('-')<<8) + 'U'] * -1 +
718        count[((int)('A')<<8) + 'A'] * -1 +
719        count[((int)('A')<<8) + 'C'] * -1 +
720        count[((int)('A')<<8) + 'G'] * 0 +
721        count[((int)('A')<<8) + 'U'] * 1 +
722        count[((int)('C')<<8) + 'C'] * -1 +
723        count[((int)('C')<<8) + 'G'] * 1 +
724        count[((int)('C')<<8) + 'U'] * -1 +
725        count[((int)('G')<<8) + 'G'] * 0 +
726        count[((int)('G')<<8) + 'U'] * 1 +
727        count[((int)('U')<<8) + 'U'] * -1;
728#endif
729
730    score /= num;
731
732    return score;
733}
734
735/*
736  Local Variables:
737  mode:c++
738  c-file-style:"stroustrup"
739  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
740  indent-tabs-mode:nil
741  fill-column:99
742  End:
743*/
744// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
745
Note: See TracBrowser for help on using the repository browser.