1 /**
2  * UGENE - Integrated Bioinformatics Tools.
3  * Copyright (C) 2008-2021 UniPro <ugene@unipro.ru>
4  * http://ugene.net
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  * MA 02110-1301, USA.
20  */
21 
22 #include "SMatrix.h"
23 
24 #include <U2Core/AppContext.h>
25 #include <U2Core/TextUtils.h>
26 
27 namespace U2 {
28 
29 #define DEFAULT_FILL_VALUE -1000000.0f
30 
SMatrix(const QString & _name,const DNAAlphabet * _alphabet,const QList<SScore> & rawMatrix,const QString & _description)31 SMatrix::SMatrix(const QString &_name, const DNAAlphabet *_alphabet, const QList<SScore> &rawMatrix, const QString &_description)
32     : name(_name), description(_description), alphabet(_alphabet) {
33     validCharacters = alphabet->getAlphabetChars();
34     TextUtils::charBounds(validCharacters.constData(), validCharacters.size(), minChar, maxChar);
35     charsInRow = maxChar - minChar + 1;
36 
37     scores.resize(charsInRow * charsInRow);
38     std::fill(scores.data(), scores.data() + scores.size(), -1000000);
39 
40     minScore = 1000000;
41     maxScore = -1000000;
42     foreach (const SScore &s, rawMatrix) {
43         int idx = getScoreIdx(s.c1, s.c2);
44         scores[idx] = s.score;
45         minScore = qMin(minScore, s.score);
46         maxScore = qMax(maxScore, s.score);
47     }
48 
49     // try to fix amino alphabet for extended symbols if needed: U(Selenocysteine) & O(Pyrrolysine)
50     if (alphabet->isAmino()) {
51         if (getScore('U', 'U') == DEFAULT_FILL_VALUE) {  // no score for 'U' symbol, use score value for 'C' (Cysteine)
52             copyCharValues('C', 'U');
53         }
54         if (getScore('O', 'O') == DEFAULT_FILL_VALUE) {  // no score for 'O' symbol, use score value for 'K' (Lysine)
55             copyCharValues('K', 'O');
56         }
57     }
58 
59     // now for all symbols in alphabet without score set the minimal score
60     for (char c1: qAsConst(validCharacters)) {
61         for (char c2: qAsConst(validCharacters)) {
62             float score = getScore(c1, c2);
63             if (score == DEFAULT_FILL_VALUE) {
64                 setScore(c1, c2, minScore);
65             }
66         }
67     }
68 }
69 
copyCharValues(char srcChar,char dstChar)70 void SMatrix::copyCharValues(char srcChar, char dstChar) {
71     for (int i = 0; i < validCharacters.size(); i++) {
72         char c = validCharacters.at(i);
73         float scoreSrc1 = getScore(srcChar, c);
74         setScore(dstChar, c, scoreSrc1);
75         float scoreSrc2 = getScore(c, srcChar);
76         setScore(c, dstChar, scoreSrc2);
77     }
78 }
79 
toQVariant() const80 QVariant SMatrix::toQVariant() const {
81     QList<QVariant> res;
82     res.append(name);
83     res.append(description);
84     res.append(alphabet->getId());
85     res.append(QChar(minChar));
86     res.append(QChar(maxChar));
87     res.append(charsInRow);
88     res.append(validCharacters);
89     res.append(double(minScore));
90     res.append(double(maxScore));
91 
92     res.append(scores.size());
93     for (int i = 0; i < scores.size(); i++) {
94         res.append(double(scores[i]));
95     }
96 
97     return res;
98 }
99 
fromQVariant(const QVariant & v)100 SMatrix SMatrix::fromQVariant(const QVariant &v) {
101     QList<QVariant> list = v.toList();
102     SMatrix m;
103 
104     int n = 0;
105     m.name = list.at(n++).toString();
106     m.description = list.at(n++).toString();
107     QString alphabetId = list.at(n++).toString();
108     m.alphabet = AppContext::getDNAAlphabetRegistry()->findById(alphabetId);
109     m.minChar = list.at(n++).toChar().toLatin1();
110     m.maxChar = list.at(n++).toChar().toLatin1();
111     m.charsInRow = list.at(n++).toInt();
112     m.validCharacters = list.at(n++).toByteArray();
113     m.minScore = (float)list.at(n++).toDouble();
114     m.maxScore = (float)list.at(n++).toDouble();
115 
116     int nScores = list.at(n++).toInt();
117     m.scores.resize(nScores);
118     for (int i = 0; i < nScores; i++) {
119         m.scores[i] = float(list.at(n++).toDouble());
120     }
121 
122     if (m.name.isEmpty() || m.alphabet == nullptr || m.validCharacters.isEmpty() || !m.validCharacters.contains(m.minChar) || !m.validCharacters.contains(m.maxChar) || m.maxChar - m.minChar + 1 != m.charsInRow) {
123         coreLog.error("Error during substitution matrix deserialization!");
124         return SMatrix();
125     }
126     return m;
127 }
128 
129 }  // namespace U2
130