Line |
Branch |
Exec |
Source |
1 |
|
|
/* BEGIN software license |
2 |
|
|
* |
3 |
|
|
* MsXpertSuite - mass spectrometry software suite |
4 |
|
|
* ----------------------------------------------- |
5 |
|
|
* Copyright(C) 2009,...,2018 Filippo Rusconi |
6 |
|
|
* |
7 |
|
|
* http://www.msxpertsuite.org |
8 |
|
|
* |
9 |
|
|
* This file is part of the MsXpertSuite project. |
10 |
|
|
* |
11 |
|
|
* The MsXpertSuite project is the successor of the massXpert project. This |
12 |
|
|
* project now includes various independent modules: |
13 |
|
|
* |
14 |
|
|
* - massXpert, model polymer chemistries and simulate mass spectrometric data; |
15 |
|
|
* - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; |
16 |
|
|
* |
17 |
|
|
* This program is free software: you can redistribute it and/or modify |
18 |
|
|
* it under the terms of the GNU General Public License as published by |
19 |
|
|
* the Free Software Foundation, either version 3 of the License, or |
20 |
|
|
* (at your option) any later version. |
21 |
|
|
* |
22 |
|
|
* This program is distributed in the hope that it will be useful, |
23 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
24 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
25 |
|
|
* GNU General Public License for more details. |
26 |
|
|
* |
27 |
|
|
* You should have received a copy of the GNU General Public License |
28 |
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
29 |
|
|
* |
30 |
|
|
* END software license |
31 |
|
|
*/ |
32 |
|
|
|
33 |
|
|
|
34 |
|
|
/////////////////////// Qt includes |
35 |
|
|
#include <QChar> |
36 |
|
|
#include <QString> |
37 |
|
|
|
38 |
|
|
|
39 |
|
|
/////////////////////// Local includes |
40 |
|
|
#include "globals.hpp" |
41 |
|
|
#include "Formula.hpp" |
42 |
|
|
|
43 |
|
|
|
44 |
|
|
namespace MsXpS |
45 |
|
|
{ |
46 |
|
|
|
47 |
|
|
namespace libXpertMass |
48 |
|
|
{ |
49 |
|
|
|
50 |
|
|
|
51 |
|
|
/*! |
52 |
|
|
\class MsXpS::libXpertMass::Formula |
53 |
|
|
\inmodule libXpertMass |
54 |
|
|
\ingroup PolChemDefBuildingdBlocks |
55 |
|
|
\inheaderfile Formula.hpp |
56 |
|
|
|
57 |
|
|
\brief The Formula class provides sophisticated abstractions to work with |
58 |
|
|
formulas. |
59 |
|
|
|
60 |
|
|
There are two peculiarities with this Formula implementation: |
61 |
|
|
|
62 |
|
|
\list |
63 |
|
|
\li The \e{Actionformula} |
64 |
|
|
\li The \e{Title} |
65 |
|
|
\endlist |
66 |
|
|
|
67 |
|
|
\e{\b{The actionformula}}: the main textual element in this Formula |
68 |
|
|
class is the \e{actionformula} (member m_formula). A formula is the description |
69 |
|
|
of the atomic composition of a compound. For example, the string \e{C2H6} is a |
70 |
|
|
formula. While the previous \e{C2H6} example describes a static chemical |
71 |
|
|
object, a Formula can also describe a dynamic chemical event, like a reaction, |
72 |
|
|
by describing what chemical entities are gained by the molecule during the |
73 |
|
|
chemical reaction (the "plus" component of the actionformula) and what chemical |
74 |
|
|
entities are lost by the molecule (the "minus" component). For example, an |
75 |
|
|
acetylation reaction can be described by the loss of \e{H2O} with gain of |
76 |
|
|
\e{CH3COOH}. The net chemical gain on the molecule will be \e{CH3CO}. In this |
77 |
|
|
example, one would thus define an actionformula in the following way: |
78 |
|
|
\e{-H20+CH3COOH}. The "minus" formula associated with the '-' action accounts |
79 |
|
|
for the leaving group of the reaction, while the "plus" formula associated with |
80 |
|
|
the '+' action accounts for the entering group of the reaction. Note that there |
81 |
|
|
is no limitation on the amount of such actions, as one could have an action |
82 |
|
|
formula like this \e{-H+CO2-H2O+C2H6}. An \e{actionformula} does not need to |
83 |
|
|
have any action sign (+ or -), and if it has no sign, the actionformula is a |
84 |
|
|
plus-signed formula by default, which is what the reader would expect for a |
85 |
|
|
standard formula. |
86 |
|
|
|
87 |
|
|
\e{\b{The title}}: the actionformula may be documented with a title: a prefix |
88 |
|
|
text enclosed in double quotes, like the following: \e{"Decomposed adenine" |
89 |
|
|
C5H4N5 +H}. This documentation element is called the \e{title}. Note that the |
90 |
|
|
presence of a title in a formula does not change anything to its workings as |
91 |
|
|
long as the \e{title} is effectively enclosed in double quotes. The title is by |
92 |
|
|
no means a required textual element for an actionformula to work correctly. It |
93 |
|
|
is mainly used in some particular context, like the calculator. An actionformula |
94 |
|
|
behaves exactly the same as a simple formula from an end user perspective. When |
95 |
|
|
created, a Formula has its formula string containing the formula (be it a pure |
96 |
|
|
formula or an actionformula). Behind the scenes, functions are called to |
97 |
|
|
separate all the '+'-associated formulas from all the '-'-associated formulas |
98 |
|
|
so that masses are correctly associated to each "leaving" or "entering" |
99 |
|
|
chemical groups. Formulas that are '-'-associated are stored in the so-called |
100 |
|
|
"minus formula", while '+'-associated ones are stored in the "plus formula". |
101 |
|
|
Note that all the formulas in Formula are QString objects. |
102 |
|
|
|
103 |
|
|
Upon parsing of the formula, the m_minusFormula and the m_plusFormula members |
104 |
|
|
are populated with formulas (in the \e{-H+CO2-H2O+C2H6} example, the |
105 |
|
|
"minus formula" would contain "H1H2O", while the "plus formula" would contain |
106 |
|
|
"CO2C2H6") and these are next used to account for the net formula. |
107 |
|
|
*/ |
108 |
|
|
|
109 |
|
|
/*! |
110 |
|
|
\enum MsXpS::libXpertMass::FormulaSplitResult |
111 |
|
|
|
112 |
|
|
This enum type specifies the result of an actionformula parsing process: |
113 |
|
|
|
114 |
|
|
\value NOT_SET |
115 |
|
|
The value was not set |
116 |
|
|
\value FAILURE |
117 |
|
|
The splitting work failed |
118 |
|
|
\value HAS_PLUS_COMPONENT |
119 |
|
|
The action formula has a plus component |
120 |
|
|
\value HAS_MINUS_COMPONENT |
121 |
|
|
The action formula has a minus component |
122 |
|
|
\value HAS_BOTH_COMPONENTS |
123 |
|
|
The action formula has both plus and minus components |
124 |
|
|
*/ |
125 |
|
|
|
126 |
|
|
|
127 |
|
|
/*! |
128 |
|
|
\variable MsXpS::libXpertMass::Formula::m_formula |
129 |
|
|
|
130 |
|
|
\brief String representing the actionformula. |
131 |
|
|
*/ |
132 |
|
|
|
133 |
|
|
/*! |
134 |
|
|
\variable MsXpS::libXpertMass::Formula::m_plusFormula |
135 |
|
|
|
136 |
|
|
\brief String representing the "plus" component of the main m_formula. |
137 |
|
|
|
138 |
|
|
This member datum is set upon parsing of m_formula. |
139 |
|
|
*/ |
140 |
|
|
|
141 |
|
|
/*! |
142 |
|
|
\variable MsXpS::libXpertMass::Formula::m_minusFormula |
143 |
|
|
|
144 |
|
|
\brief String representing the "minus" component of the main m_minusFormula. |
145 |
|
|
|
146 |
|
|
This member datum is set upon parsing of m_formula. |
147 |
|
|
*/ |
148 |
|
|
|
149 |
|
|
/*! |
150 |
|
|
\variable MsXpS::libXpertMass::Formula::m_symbolCountMap |
151 |
|
|
|
152 |
|
|
\brief Map relating the symbols (as keys) found in the formula and their |
153 |
|
|
counts (atoms, in fact, as values). |
154 |
|
|
|
155 |
|
|
Note that the count value type is double, which allows for interesting |
156 |
|
|
things to be done with Formula. Also, the count value might be negative if the |
157 |
|
|
net mass of an actionformula is negative. |
158 |
|
|
|
159 |
|
|
\sa Formula::splitActionParts() |
160 |
|
|
*/ |
161 |
|
|
|
162 |
|
|
/*! |
163 |
|
|
\variable MsXpS::libXpertMass::Formula::mcsp_isotopicData |
164 |
|
|
|
165 |
|
|
\brief The isotopic data that the formula is based on. |
166 |
|
|
*/ |
167 |
|
|
|
168 |
|
|
|
169 |
|
|
/*! |
170 |
|
|
\variable int MsXpS::libXpertMass::Formula::m_forceCountIndex |
171 |
|
|
|
172 |
|
|
\brief The m_forceCountIndex tells if when defining a chemical composition |
173 |
|
|
formula, the index '1' is required when the count of a symbol is not specified |
174 |
|
|
and thus considered to be '1' by default. If true, water should be described as |
175 |
|
|
"H2O1", if false, it might be described as "H2O". |
176 |
|
|
*/ |
177 |
|
|
|
178 |
|
|
|
179 |
|
|
/*! |
180 |
|
|
\brief Constructs a formula initialized with the \a formula_string |
181 |
|
|
actionformula string. |
182 |
|
|
|
183 |
|
|
\a formula_string needs not be an actionformula, but it might be an |
184 |
|
|
actionformula. This formula gets copied into the \c m_formula without any |
185 |
|
|
processing afterwards. |
186 |
|
|
*/ |
187 |
|
68405 |
Formula::Formula(const QString &formula_string) : m_formula{formula_string} |
188 |
|
|
{ |
189 |
|
68405 |
} |
190 |
|
|
|
191 |
|
|
/*! |
192 |
|
|
\brief Constructs a formula as a copy of \a other. |
193 |
|
|
|
194 |
|
|
The copy is deep with \e{all} the data copied from \a other to the new |
195 |
|
|
formula. There is no processing afterwards. |
196 |
|
|
*/ |
197 |
|
4522 |
Formula::Formula(const Formula &other) |
198 |
|
4522 |
: m_formula{other.m_formula}, |
199 |
|
4522 |
m_plusFormula{other.m_plusFormula}, |
200 |
|
9044 |
m_minusFormula{other.m_minusFormula} |
201 |
|
|
{ |
202 |
1/2
✓ Branch 1 taken 4522 times.
✗ Branch 2 not taken.
|
4522 |
m_symbolCountMap = other.m_symbolCountMap; |
203 |
|
4522 |
} |
204 |
|
|
|
205 |
|
|
|
206 |
|
|
/*! |
207 |
|
|
\brief Destructs this formula. |
208 |
|
|
|
209 |
|
|
There is nothing to be delete explicitly. |
210 |
|
|
*/ |
211 |
|
98006 |
Formula::~Formula() |
212 |
|
|
{ |
213 |
|
98006 |
} |
214 |
|
|
|
215 |
|
|
/*! |
216 |
|
|
\brief Initializes all the member data of this formula by copying to it the |
217 |
|
|
data from \a other. |
218 |
|
|
|
219 |
|
|
The copy is deep with \e{all} the data from \a other being copied into this |
220 |
|
|
formula. |
221 |
|
|
|
222 |
|
|
There is no processing afterwards. |
223 |
|
|
*/ |
224 |
|
|
Formula & |
225 |
|
30644 |
Formula::operator=(const Formula &other) |
226 |
|
|
{ |
227 |
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30644 times.
|
30644 |
if(&other == this) |
228 |
|
✗ |
return *this; |
229 |
|
|
|
230 |
|
30644 |
m_formula = other.m_formula; |
231 |
|
30644 |
m_plusFormula = other.m_plusFormula; |
232 |
|
30644 |
m_minusFormula = other.m_minusFormula; |
233 |
|
30644 |
m_symbolCountMap = other.m_symbolCountMap; |
234 |
|
|
|
235 |
|
30644 |
return *this; |
236 |
|
|
} |
237 |
|
|
|
238 |
|
|
|
239 |
|
|
/*! Sets the actionformula \a formula to this Formula. |
240 |
|
|
|
241 |
|
|
The \a formula is copied to this m_formula. No other processing is |
242 |
|
|
performed afterwards. |
243 |
|
|
*/ |
244 |
|
|
void |
245 |
|
1236 |
Formula::setFormula(const QString &formula) |
246 |
|
|
{ |
247 |
|
1236 |
m_formula = formula; |
248 |
|
1236 |
} |
249 |
|
|
|
250 |
|
|
/*! Sets the actionformula from \a formula to this Formula. |
251 |
|
|
|
252 |
|
|
The actionformula from \a formula is copied to this m_formula. No |
253 |
|
|
processing is performed afterwards. |
254 |
|
|
*/ |
255 |
|
|
void |
256 |
|
✗ |
Formula::setFormula(const Formula &formula) |
257 |
|
|
{ |
258 |
|
✗ |
m_formula = formula.m_formula; |
259 |
|
✗ |
} |
260 |
|
|
|
261 |
|
|
/*! |
262 |
|
|
\brief Appends to this formula the \a formula. |
263 |
|
|
|
264 |
|
|
The \a formula string is appended to the m_formula without check. No |
265 |
|
|
processing is performed afterwards. The \a formula is copied to a |
266 |
|
|
temporary formula that is stripped of its spaces, both in the formula and |
267 |
|
|
before and after it before it is appended to m_formula. |
268 |
|
|
*/ |
269 |
|
|
void |
270 |
|
4 |
Formula::appendFormula(const QString &formula) |
271 |
|
|
{ |
272 |
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 |
QString local_formula = formula.simplified(); |
273 |
|
|
|
274 |
|
|
// Remove the spaces before appending. |
275 |
3/6
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
|
4 |
local_formula.remove(QRegularExpression("\\s+")); |
276 |
|
|
|
277 |
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 |
m_formula.append(local_formula); |
278 |
|
4 |
} |
279 |
|
|
|
280 |
|
|
|
281 |
|
|
/*! |
282 |
|
|
\brief Returns the actionformula. |
283 |
|
|
*/ |
284 |
|
|
QString |
285 |
|
1460 |
Formula::toString() const |
286 |
|
|
{ |
287 |
|
1460 |
return m_formula; |
288 |
|
|
} |
289 |
|
|
|
290 |
|
|
/*! |
291 |
|
|
\brief Sets m_forceCountIndex to \a forceCountIndex. |
292 |
|
|
|
293 |
|
|
When a formula contains a chemical element in a single copy, it is standard |
294 |
|
|
practice to omit the count index: H2O is the same as H2O1. If forceCountIndex is |
295 |
|
|
true, then the formula has to be in the form H2O1. This is required for some |
296 |
|
|
specific calculations. |
297 |
|
|
*/ |
298 |
|
|
void |
299 |
|
24 |
Formula::setForceCountIndex(bool forceCountIndex) |
300 |
|
|
{ |
301 |
|
24 |
m_forceCountIndex = forceCountIndex; |
302 |
|
24 |
} |
303 |
|
|
|
304 |
|
|
/*! |
305 |
|
|
\brief Clears \e{all} the formula member data. |
306 |
|
|
*/ |
307 |
|
|
void |
308 |
|
✗ |
Formula::clear() |
309 |
|
|
{ |
310 |
|
✗ |
m_formula.clear(); |
311 |
|
✗ |
m_plusFormula.clear(); |
312 |
|
✗ |
m_minusFormula.clear(); |
313 |
|
|
|
314 |
|
✗ |
m_symbolCountMap.clear(); |
315 |
|
✗ |
} |
316 |
|
|
|
317 |
|
|
/*! |
318 |
|
|
\brief Sets the m_plusFormula formula to \a formula. |
319 |
|
|
*/ |
320 |
|
|
void |
321 |
|
✗ |
Formula::setPlusFormula(const QString &formula) |
322 |
|
|
{ |
323 |
|
✗ |
m_plusFormula = formula; |
324 |
|
✗ |
} |
325 |
|
|
|
326 |
|
|
/*! |
327 |
|
|
\brief Returns the m_plusFormula formula. |
328 |
|
|
*/ |
329 |
|
|
QString |
330 |
|
12 |
Formula::plusFormula() const |
331 |
|
|
{ |
332 |
|
12 |
return m_plusFormula; |
333 |
|
|
} |
334 |
|
|
|
335 |
|
|
/*! |
336 |
|
|
\brief Sets the m_minusFormula formula to \a formula. |
337 |
|
|
*/ |
338 |
|
|
void |
339 |
|
✗ |
Formula::setMinusFormula(const QString &formula) |
340 |
|
|
{ |
341 |
|
✗ |
m_minusFormula = formula; |
342 |
|
✗ |
} |
343 |
|
|
|
344 |
|
|
/*! |
345 |
|
|
\brief Returns the m_minusFormula formula. |
346 |
|
|
*/ |
347 |
|
|
QString |
348 |
|
12 |
Formula::minusFormula() const |
349 |
|
|
{ |
350 |
|
12 |
return m_minusFormula; |
351 |
|
|
} |
352 |
|
|
|
353 |
|
|
|
354 |
|
|
/*! |
355 |
|
|
\brief Returns the m_symbolCountMap that relates chemical symbols with |
356 |
|
|
corresponding counts. |
357 |
|
|
*/ |
358 |
|
|
const std::map<QString, double> & |
359 |
|
4 |
Formula::symbolCountMap() const |
360 |
|
|
{ |
361 |
|
4 |
return m_symbolCountMap; |
362 |
|
|
} |
363 |
|
|
|
364 |
|
|
/*! Returns true if this Formula and \a other are identical, false otherwise. |
365 |
|
|
|
366 |
|
|
The comparison is only performed on the m_formula actionformula, not on any |
367 |
|
|
other member data that derived from processing of m_formula. |
368 |
|
|
*/ |
369 |
|
|
bool |
370 |
|
2708 |
Formula::operator==(const Formula &other) const |
371 |
|
|
{ |
372 |
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2708 times.
|
2708 |
if(&other == this) |
373 |
|
✗ |
return true; |
374 |
|
|
|
375 |
2/2
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 2692 times.
|
2708 |
if(m_formula != other.m_formula) |
376 |
|
16 |
return false; |
377 |
|
|
|
378 |
|
2692 |
return true; |
379 |
|
|
} |
380 |
|
|
|
381 |
|
|
/*! Returns true if this Formula and \a other are different, false otherwise. |
382 |
|
|
|
383 |
|
|
Returns the negated result of operator==(). |
384 |
|
|
*/ |
385 |
|
|
bool |
386 |
|
2708 |
Formula::operator!=(const Formula &other) const |
387 |
|
|
{ |
388 |
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2708 times.
|
2708 |
if(&other == this) |
389 |
|
✗ |
return false; |
390 |
|
|
|
391 |
|
2708 |
return !operator==(other); |
392 |
|
|
} |
393 |
|
|
|
394 |
|
|
|
395 |
|
|
/*! |
396 |
|
|
\brief Calls actions(const QString &formula) on this Formula's |
397 |
|
|
actionformula m_formula. Returns '+' if it only contains "plus" |
398 |
|
|
elements or '-' if at least one "minus" element was found. |
399 |
|
|
|
400 |
|
|
If m_formula contains no sign at all, then it is considered to contain only |
401 |
|
|
'+' elements and the function returns '+'. If at least one element is found |
402 |
|
|
associated to a '-', then the "minus" action prevails and the function |
403 |
|
|
returns '-'. |
404 |
|
|
|
405 |
|
|
\sa actions(const QString &formula), splitActionParts() |
406 |
|
|
*/ |
407 |
|
|
QChar |
408 |
|
✗ |
Formula::actions() const |
409 |
|
|
{ |
410 |
|
✗ |
return actions(m_formula); |
411 |
|
|
} |
412 |
|
|
|
413 |
|
|
/*! |
414 |
|
|
\brief Returns '+' if \a formula only contains "plus" elements or '-' |
415 |
|
|
if at least one "minus" element was found. |
416 |
|
|
|
417 |
|
|
If \a formula contains no sign at all, then it is considered to contain only |
418 |
|
|
'+' elements and the function returns '+'. If at least one element is found |
419 |
|
|
associated to a '-', then the "minus" action prevails and the function |
420 |
|
|
returns '-'. |
421 |
|
|
|
422 |
|
|
\sa actions(), splitActionParts() |
423 |
|
|
*/ |
424 |
|
|
QChar |
425 |
|
✗ |
Formula::actions(const QString &formula) |
426 |
|
|
{ |
427 |
|
✗ |
double minusCount = formula.count('-', Qt::CaseInsensitive); |
428 |
|
|
|
429 |
|
✗ |
return (minusCount == 0 ? '+' : '-'); |
430 |
|
|
} |
431 |
|
|
|
432 |
|
|
/*! |
433 |
|
|
\brief Removes the title from the member actionformula. |
434 |
|
|
|
435 |
|
|
The \e{title} of a formula is the string, enclosed in |
436 |
|
|
double quotes, that is located in front of the actual chemical |
437 |
|
|
actionformula. This function removes that \e{title} string from the |
438 |
|
|
member actionformula using a QRegularExpression. |
439 |
|
|
|
440 |
|
|
Returns the count of removed characters. |
441 |
|
|
*/ |
442 |
|
|
int |
443 |
|
✗ |
Formula::removeTitle() |
444 |
|
|
{ |
445 |
|
✗ |
int length = m_formula.length(); |
446 |
|
|
|
447 |
|
✗ |
m_formula.remove(QRegularExpression("\".*\"")); |
448 |
|
|
|
449 |
|
✗ |
return (length - m_formula.length()); |
450 |
|
|
} |
451 |
|
|
|
452 |
|
|
|
453 |
|
|
/*! |
454 |
|
|
\brief Removes \e{all} the space characters from the member actionformula. |
455 |
|
|
|
456 |
|
|
Spaces can be placed anywhere in formula for more readability. However, it |
457 |
|
|
might be required that these character spaces be removed. This function does |
458 |
|
|
just this, using a QRegularExpression. |
459 |
|
|
|
460 |
|
|
Returns the number of removed characters. |
461 |
|
|
*/ |
462 |
|
|
int |
463 |
|
✗ |
Formula::removeSpaces() |
464 |
|
|
{ |
465 |
|
✗ |
int length = m_formula.length(); |
466 |
|
|
|
467 |
|
|
// We want to remove all the possibly-existing spaces. |
468 |
|
|
|
469 |
|
✗ |
m_formula.remove(QRegularExpression("\\s+")); |
470 |
|
|
|
471 |
|
|
// Return the number of removed characters. |
472 |
|
✗ |
return (length - m_formula.length()); |
473 |
|
|
} |
474 |
|
|
|
475 |
|
|
/*! |
476 |
|
|
\brief Tells the "plus" ('+') and "minus" ('-') parts in the member |
477 |
|
|
actinformula. |
478 |
|
|
|
479 |
|
|
Parses the m_formula actionformula and separates all the minus components |
480 |
|
|
of that actionformula from all the plus components. The different components |
481 |
|
|
are set to their corresponding formula (m_minusFormula and m_plusFormula). |
482 |
|
|
|
483 |
|
|
At the end of the split work, each sub-formula (plus- and/or minus-) |
484 |
|
|
is actually parsed for validity, using the \a isotopic_data_csp IsotopicData |
485 |
|
|
as reference. |
486 |
|
|
|
487 |
|
|
If \a times is not 1, then the accounting of the plus/minus formulas is |
488 |
|
|
compounded by this factor. |
489 |
|
|
|
490 |
|
|
If \a store is true, the symbol/count data obtained while |
491 |
|
|
parsing of the plus/minus actionformula components are stored |
492 |
|
|
(m_symbolCountMap). |
493 |
|
|
|
494 |
|
|
If \a reset is true, the symbol/count data are reset before the parsing of |
495 |
|
|
the actionformula. Setting this parameter to false may be useful if the |
496 |
|
|
caller needs to "accumulate" the accounting of the formulas. |
497 |
|
|
|
498 |
|
|
The parsing of the actionformula is performed by performing its deconstruction |
499 |
|
|
using m_subFormulaRegExp. |
500 |
|
|
|
501 |
|
|
Returns MsXpS::libXpertMass::FormulaSplitResult::FAILURE if the splitting |
502 |
|
|
failed, MsXpS::libXpertMass::FormulaSplitResult::HAS_PLUS_COMPONENT if at least |
503 |
|
|
one of the components of the actionformula was found to be of type plus, |
504 |
|
|
MsXpS::libXpertMass::FormulaSplitResult::HAS_MINUS_COMPONENT if at least one of |
505 |
|
|
the components of the actionformula was found to be of type minus. The result |
506 |
|
|
can be an OR'ing of both values |
507 |
|
|
(MsXpS::libXpertMass::FormulaSplitResult::HAS_BOTH_COMPONENTS) in the m_formula |
508 |
|
|
actionformula. |
509 |
|
|
*/ |
510 |
|
|
int |
511 |
|
44248 |
Formula::splitActionParts(IsotopicDataCstSPtr isotopic_data_csp, |
512 |
|
|
double times, |
513 |
|
|
bool store, |
514 |
|
|
bool reset) |
515 |
|
|
{ |
516 |
2/4
✓ Branch 2 taken 44248 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 44248 times.
|
44248 |
if(!isotopic_data_csp->size()) |
517 |
|
✗ |
qFatal("Programming error. The isotopic data cannot be empty."); |
518 |
|
|
|
519 |
|
44248 |
int formula_split_result = static_cast<int>(FormulaSplitResult::NOT_SET); |
520 |
|
|
|
521 |
|
|
// We are asked to put all the '+' components of the formula |
522 |
|
|
// into corresponding formula and the same for the '-' components. |
523 |
|
|
|
524 |
|
44248 |
m_plusFormula.clear(); |
525 |
|
44248 |
m_minusFormula.clear(); |
526 |
|
|
|
527 |
2/2
✓ Branch 0 taken 19480 times.
✓ Branch 1 taken 24768 times.
|
44248 |
if(reset) |
528 |
|
19480 |
m_symbolCountMap.clear(); |
529 |
|
|
|
530 |
|
|
// Because the formula that we are analyzing might contain a title |
531 |
|
|
// and spaces , we first remove these. But make a local copy of |
532 |
|
|
// the member datum. |
533 |
|
|
|
534 |
|
44248 |
QString formula = m_formula; |
535 |
|
|
|
536 |
|
|
// qDebug() << "splitActionParts before working:" |
537 |
|
|
// << "m_formula:" << m_formula; |
538 |
|
|
|
539 |
|
|
// One formula can be like this: |
540 |
|
|
|
541 |
|
|
// "Decomposed adenine" C5H4N5 +H |
542 |
|
|
|
543 |
|
|
// The "Decomposed adenine" is the title |
544 |
|
|
// The C5H4N5 +H is the formula. |
545 |
|
|
|
546 |
3/6
✓ Branch 1 taken 44248 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 44248 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 44248 times.
✗ Branch 8 not taken.
|
44248 |
formula.remove(QRegularExpression("\".*\"")); |
547 |
|
|
|
548 |
|
|
// We want to remove all the possibly-existing spaces. |
549 |
|
|
|
550 |
3/6
✓ Branch 1 taken 44248 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 44248 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 44248 times.
✗ Branch 8 not taken.
|
44248 |
formula.remove(QRegularExpression("\\s+")); |
551 |
|
|
|
552 |
|
|
#if 0 |
553 |
|
|
|
554 |
|
|
// This old version tried to save computing work, but it then anyways |
555 |
|
|
// calls for parsing of the formula which is the most computing-intensive |
556 |
|
|
// part. Thus we now rely on the regular expression to simultaneously |
557 |
|
|
// check the syntax, divide the formula into its '+' and '-' parts, |
558 |
|
|
// and finally check that the symbol is known to the isotopic data. |
559 |
|
|
^ |
560 |
|
|
// If the formula does not contain any '-' character, then we |
561 |
|
|
// can approximate that all the formula is a '+' formula, that is, a |
562 |
|
|
// plusFormula: |
563 |
|
|
|
564 |
|
|
if(actions() == '+') |
565 |
|
|
{ |
566 |
|
|
// qDebug() << "Only plus actions."; |
567 |
|
|
|
568 |
|
|
m_plusFormula.append(formula); |
569 |
|
|
|
570 |
|
|
// At this point we want to make sure that we have a correct |
571 |
|
|
// formula. Remove all the occurrences of the '+' sign. |
572 |
|
|
m_plusFormula.replace(QString("+"), QString("")); |
573 |
|
|
|
574 |
|
|
if(m_plusFormula.length() > 0) |
575 |
|
|
{ |
576 |
|
|
// qDebug() << "splitActionParts: with m_plusFormula:" << |
577 |
|
|
// m_plusFormula; |
578 |
|
|
|
579 |
|
|
if(!parse(isotopic_data_csp, m_plusFormula, times, store, reset)) |
580 |
|
|
return FormulaSplitResult::FAILURE; |
581 |
|
|
else |
582 |
|
|
return FORMULA_SPLIT_PLUS; |
583 |
|
|
} |
584 |
|
|
} |
585 |
|
|
// End of |
586 |
|
|
// if(actions() == '+') |
587 |
|
|
|
588 |
|
|
// If we did not return at previous block that means there are at least one |
589 |
|
|
// '-' component in the formula. we truly have to iterate in the formula... |
590 |
|
|
#endif |
591 |
|
|
|
592 |
|
|
|
593 |
|
|
// See the explanations in the header file for the member datum |
594 |
|
|
// m_subFormulaRegExp and its use with globalMatch(). One thing that is |
595 |
|
|
// important to see, is that the RegExp matches a triad : [ [sign or not] |
596 |
|
|
// [symbol] [count] ], so, if we have, says, formula "-H2O", it would match: |
597 |
|
|
|
598 |
|
|
// First '-' 'H' '2' |
599 |
|
|
// Second <no sign> 'O' <no count = 1> |
600 |
|
|
|
601 |
|
|
// The problem is that at second match, the algo thinks that O1 is a + |
602 |
|
|
// formula, while in fact it is part of a larger minus formula: -H2O. So we |
603 |
|
|
// need to check if, after a '-' in a formula, there comes or not a '+'. If so |
604 |
|
|
// we close the minus formula and start a plus formula, if not, we continue |
605 |
|
|
// adding matches to the minus formula. |
606 |
|
|
|
607 |
|
|
// qDebug() << "Now regex parsing with globalMatch feature of formula:" |
608 |
|
|
// << formula; |
609 |
|
|
|
610 |
|
44248 |
bool was_minus_formula = false; |
611 |
|
|
|
612 |
|
44248 |
for(const QRegularExpressionMatch &match : |
613 |
5/8
✓ Branch 1 taken 44248 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 44248 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 139180 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 139180 times.
✓ Branch 13 taken 44248 times.
|
366856 |
subFormulaRegExp.globalMatch(formula)) |
614 |
|
|
{ |
615 |
1/2
✓ Branch 1 taken 139180 times.
✗ Branch 2 not taken.
|
139180 |
QString sub_match = match.captured(0); |
616 |
|
|
|
617 |
|
|
// qDebug() << "Entering [+-]?<symbol><count?> sub-match:" << sub_match; |
618 |
|
|
|
619 |
1/2
✓ Branch 1 taken 139180 times.
✗ Branch 2 not taken.
|
139180 |
QString sign = match.captured(1); |
620 |
1/2
✓ Branch 1 taken 139180 times.
✗ Branch 2 not taken.
|
139180 |
QString symbol = match.captured(2); |
621 |
1/2
✓ Branch 1 taken 139180 times.
✗ Branch 2 not taken.
|
139180 |
QString count_as_string = match.captured(3); |
622 |
|
139180 |
double count_as_double = 1.0; |
623 |
|
|
|
624 |
2/2
✓ Branch 1 taken 134284 times.
✓ Branch 2 taken 4896 times.
|
139180 |
if(!count_as_string.isEmpty()) |
625 |
|
|
{ |
626 |
|
134284 |
bool ok = false; |
627 |
1/2
✓ Branch 1 taken 134284 times.
✗ Branch 2 not taken.
|
134284 |
count_as_double = count_as_string.toDouble(&ok); |
628 |
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 134284 times.
|
134284 |
if(!ok) |
629 |
|
✗ |
return static_cast<int>(FormulaSplitResult::FAILURE); |
630 |
|
|
} |
631 |
|
|
else |
632 |
|
|
{ |
633 |
1/2
✓ Branch 1 taken 4896 times.
✗ Branch 2 not taken.
|
4896 |
count_as_string = "1"; |
634 |
|
|
} |
635 |
|
|
|
636 |
|
|
// Check that the symbol is known to the isotopic data. |
637 |
|
|
// qDebug() << "The symbol:" << symbol << "with count:" << |
638 |
|
|
// count_as_double; |
639 |
|
|
|
640 |
2/4
✓ Branch 2 taken 139180 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 139180 times.
|
139180 |
if(!isotopic_data_csp->containsSymbol(symbol)) |
641 |
|
|
{ |
642 |
|
|
// qDebug() << "The symbol is not known to the Isotopic data table."; |
643 |
|
✗ |
return static_cast<int>(FormulaSplitResult::FAILURE); |
644 |
|
|
} |
645 |
|
|
|
646 |
|
|
// Determine if there was a sign |
647 |
2/2
✓ Branch 1 taken 12312 times.
✓ Branch 2 taken 126868 times.
|
139180 |
if(sign == "-") |
648 |
|
|
{ |
649 |
|
|
// qDebug() << "Appending found minus formula:" |
650 |
|
|
// << QString("%1%2").arg(symbol).arg(count_as_string); |
651 |
|
|
|
652 |
1/2
✓ Branch 1 taken 12312 times.
✗ Branch 2 not taken.
|
12312 |
m_minusFormula.append( |
653 |
3/6
✓ Branch 1 taken 12312 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12312 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 12312 times.
✗ Branch 8 not taken.
|
49248 |
QString("%1%2").arg(symbol).arg(count_as_string)); |
654 |
|
|
|
655 |
|
12312 |
formula_split_result |= |
656 |
|
|
static_cast<int>(FormulaSplitResult::HAS_MINUS_COMPONENT); |
657 |
|
|
|
658 |
2/2
✓ Branch 0 taken 3676 times.
✓ Branch 1 taken 8636 times.
|
12312 |
if(store) |
659 |
|
|
{ |
660 |
|
|
// qDebug() << "Accounting symbol / count pair:" << symbol << "/" |
661 |
|
|
// << count_as_double * static_cast<double>(times); |
662 |
|
|
|
663 |
|
3676 |
accountSymbolCountPair( |
664 |
1/2
✓ Branch 1 taken 3676 times.
✗ Branch 2 not taken.
|
3676 |
symbol, -1 * count_as_double * static_cast<double>(times)); |
665 |
|
|
|
666 |
|
|
// qDebug() << " ...done."; |
667 |
|
|
} |
668 |
|
|
|
669 |
|
|
// Let next round know that we are inside a minus formula group. |
670 |
|
12312 |
was_minus_formula = true; |
671 |
|
|
} |
672 |
6/6
✓ Branch 1 taken 123796 times.
✓ Branch 2 taken 3072 times.
✓ Branch 3 taken 5600 times.
✓ Branch 4 taken 118196 times.
✓ Branch 5 taken 5600 times.
✓ Branch 6 taken 121268 times.
|
126868 |
else if(sign.isEmpty() && was_minus_formula) |
673 |
|
|
{ |
674 |
|
|
// qDebug() << "Appending new unsigned formula to the minus formula:" |
675 |
|
|
// << QString("%1%2").arg(symbol).arg(count_as_string); |
676 |
|
|
|
677 |
1/2
✓ Branch 1 taken 5600 times.
✗ Branch 2 not taken.
|
5600 |
m_minusFormula.append( |
678 |
3/6
✓ Branch 1 taken 5600 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5600 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5600 times.
✗ Branch 8 not taken.
|
22400 |
QString("%1%2").arg(symbol).arg(count_as_string)); |
679 |
|
|
|
680 |
2/2
✓ Branch 0 taken 884 times.
✓ Branch 1 taken 4716 times.
|
5600 |
if(store) |
681 |
|
|
{ |
682 |
|
|
// qDebug() << "Accounting symbol / count pair:" << symbol << "/" |
683 |
|
|
// << count_as_double * static_cast<double>(times); |
684 |
|
|
|
685 |
|
884 |
accountSymbolCountPair( |
686 |
1/2
✓ Branch 1 taken 884 times.
✗ Branch 2 not taken.
|
884 |
symbol, -1 * count_as_double * static_cast<double>(times)); |
687 |
|
|
|
688 |
|
|
// qDebug() << " ...done."; |
689 |
|
|
} |
690 |
|
|
|
691 |
|
|
// Let next round know that we are still inside a minus formula group. |
692 |
|
5600 |
was_minus_formula = true; |
693 |
|
|
} |
694 |
|
|
else |
695 |
|
|
// Either there was a '+' sign or there was no sign, but |
696 |
|
|
// we were not continuing a minus formula, thus we are parsing |
697 |
|
|
// a true '+' formula. |
698 |
|
|
{ |
699 |
|
|
// qDebug() << "Appending found plus formula:" |
700 |
|
|
// << QString("%1%2").arg(symbol).arg(count_as_string); |
701 |
|
|
|
702 |
1/2
✓ Branch 1 taken 121268 times.
✗ Branch 2 not taken.
|
121268 |
m_plusFormula.append( |
703 |
3/6
✓ Branch 1 taken 121268 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 121268 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 121268 times.
✗ Branch 8 not taken.
|
485072 |
QString("%1%2").arg(symbol).arg(count_as_string)); |
704 |
|
|
|
705 |
|
121268 |
formula_split_result |= |
706 |
|
|
static_cast<int>(FormulaSplitResult::HAS_PLUS_COMPONENT); |
707 |
|
|
|
708 |
2/2
✓ Branch 0 taken 60732 times.
✓ Branch 1 taken 60536 times.
|
121268 |
if(store) |
709 |
|
|
{ |
710 |
|
|
// qDebug() << "Accounting symbol / count pair:" << symbol << "/" |
711 |
|
|
// << count_as_double * static_cast<double>(times); |
712 |
|
|
|
713 |
1/2
✓ Branch 1 taken 60732 times.
✗ Branch 2 not taken.
|
60732 |
accountSymbolCountPair( |
714 |
|
|
symbol, count_as_double * static_cast<double>(times)); |
715 |
|
|
|
716 |
|
|
// qDebug() << " ...done."; |
717 |
|
|
} |
718 |
|
|
|
719 |
|
121268 |
was_minus_formula = false; |
720 |
|
|
} |
721 |
6/12
✓ Branch 1 taken 139180 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 139180 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 139180 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 139180 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 44248 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 44248 times.
✗ Branch 17 not taken.
|
183428 |
} |
722 |
|
|
|
723 |
|
|
// qDebug() << "Formula" << formula << "splits" |
724 |
|
|
//<< "(+)" << m_plusFormula << "(-)" << m_minusFormula; |
725 |
|
|
|
726 |
|
44248 |
return formula_split_result; |
727 |
|
44248 |
} |
728 |
|
|
|
729 |
|
|
/*! |
730 |
|
|
\brief Accounts for \a symbol and corresponding \a count in the member map. |
731 |
|
|
|
732 |
|
|
The m_symbolCountMap relates each atom (chemical element) symbol with its |
733 |
|
|
occurrence count as encountered while parsing the member actionformula. |
734 |
|
|
|
735 |
|
|
If the symbol was not encountered yet, a new key/value pair is created. |
736 |
|
|
Otherwise, the count value is updated. |
737 |
|
|
|
738 |
|
|
Returns the new count status for \a symbol. |
739 |
|
|
*/ |
740 |
|
|
double |
741 |
|
65292 |
Formula::accountSymbolCountPair(const QString &symbol, double count) |
742 |
|
|
{ |
743 |
|
|
// We receive a symbol and we need to account for it count count in the member |
744 |
|
|
// symbol count map (count might be < 0). |
745 |
|
|
|
746 |
|
|
// Try to insert the new symbol/count pairinto the map. Check if that was done |
747 |
|
|
// or not. If the result.second is false, then that means the the insert |
748 |
|
|
// function did not perform because a pair by that symbol existed already. In |
749 |
|
|
// that case we just need to increment the count for the the pair. |
750 |
|
|
|
751 |
|
|
// qDebug() << "Accounting symbol:" << symbol << "for count:" << count; |
752 |
|
|
|
753 |
|
65292 |
double new_count = 0; |
754 |
|
|
|
755 |
|
|
std::pair<std::map<QString, double>::iterator, bool> res = |
756 |
1/2
✓ Branch 2 taken 65292 times.
✗ Branch 3 not taken.
|
65292 |
m_symbolCountMap.insert(std::pair<QString, double>(symbol, count)); |
757 |
|
|
|
758 |
2/2
✓ Branch 0 taken 400 times.
✓ Branch 1 taken 64892 times.
|
65292 |
if(!res.second) |
759 |
|
|
{ |
760 |
|
|
// qDebug() << "The symbol was already in the symbol/count map. with |
761 |
|
|
// count:" |
762 |
|
|
// << res.first->second; |
763 |
|
|
|
764 |
|
|
// One pair by that symbol key existed already, just update the count and |
765 |
|
|
// store that value for reporting. |
766 |
|
400 |
res.first->second += count; |
767 |
|
400 |
new_count = res.first->second; |
768 |
|
|
// new_count might be <= 0. |
769 |
|
|
|
770 |
|
|
// qDebug() << "For symbol" << symbol << "the new count:" << new_count; |
771 |
|
|
} |
772 |
|
|
else |
773 |
|
|
{ |
774 |
|
|
// qDebug() << "Symbol" << symbol |
775 |
|
|
// << "was not there already, setting the count to:" << count; |
776 |
|
|
|
777 |
|
|
// We just effectively added during the insert call above a new pair to |
778 |
|
|
// the map by key symbol with value count. |
779 |
|
64892 |
new_count = count; |
780 |
|
|
} |
781 |
|
|
|
782 |
|
|
// We should check if the symbol has now a count of 0. In that case, we remove |
783 |
|
|
// the symbol altogether because we do not want to list naught symbols in the |
784 |
|
|
// final formula. |
785 |
|
|
|
786 |
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 65264 times.
|
65292 |
if(!new_count) |
787 |
|
|
{ |
788 |
|
|
// qDebug() << "For symbol" << symbol |
789 |
|
|
//<< "the new count is 0. Thus we erase the map item altogether."; |
790 |
|
|
|
791 |
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
|
28 |
m_symbolCountMap.erase(symbol); |
792 |
|
|
} |
793 |
|
|
|
794 |
|
|
// Update what's the text of the formula to represent what is in |
795 |
|
|
// atomCount list. |
796 |
|
|
|
797 |
|
|
// qDebug() << "The formula now can be reduced to:" << elementalComposition(); |
798 |
|
|
|
799 |
|
65292 |
return new_count; |
800 |
|
|
} |
801 |
|
|
|
802 |
|
|
/*! |
803 |
|
|
\brief Returns a reference to the atom symbol / count member map. |
804 |
|
|
*/ |
805 |
|
|
const std::map<QString, double> |
806 |
|
✗ |
Formula::getSymbolCountMap() const |
807 |
|
|
{ |
808 |
|
✗ |
return m_symbolCountMap; |
809 |
|
|
} |
810 |
|
|
|
811 |
|
|
/*! |
812 |
|
|
\brief Accounts the actionformula in \a text using \a isotopic_data_csp as |
813 |
|
|
reference data using \a times as a compounding factor. |
814 |
|
|
|
815 |
|
|
The \a text formula is converted into a temporary Formula and processed: |
816 |
|
|
|
817 |
|
|
\list |
818 |
|
|
\li First validate() is called on the temporary formula, with storage of the |
819 |
|
|
symbol/count data; |
820 |
|
|
\li The symbol/count data thus generated is used to update the member map. |
821 |
|
|
\endlist |
822 |
|
|
|
823 |
|
|
Returns the member symbol/count m_symbolCountMap size. |
824 |
|
|
*/ |
825 |
|
|
std::size_t |
826 |
|
✗ |
Formula::accountFormula(const QString &text, |
827 |
|
|
IsotopicDataCstSPtr isotopic_data_csp, |
828 |
|
|
double times) |
829 |
|
|
{ |
830 |
|
|
// qDebug() << "Accounting in this formula:" << m_formula |
831 |
|
|
//<< "the external formula:" << text; |
832 |
|
|
|
833 |
|
|
// qDebug() << "Before having merged the external formula's map into this one, |
834 |
|
|
// " "this one has size:" |
835 |
|
|
//<< m_symbolCountMap.size(); |
836 |
|
|
|
837 |
|
|
// We get a formula as an elemental composition text string and we want to |
838 |
|
|
// account for that formula in *this formula. |
839 |
|
|
|
840 |
|
|
// First off, validate the text. |
841 |
|
|
|
842 |
|
✗ |
Formula formula(text); |
843 |
|
|
|
844 |
|
|
// The formula is asked to validate with storage of the found symbol/count |
845 |
|
|
// pairs and with resetting of the previous contents of the symbol/count map. |
846 |
|
✗ |
if(!formula.validate(isotopic_data_csp, true, true)) |
847 |
|
|
{ |
848 |
|
✗ |
qDebug() << "Formula:" << text << "failed to validate."; |
849 |
|
✗ |
return -1; |
850 |
|
|
} |
851 |
|
|
|
852 |
|
|
// Now, for each item in the formula's symbol/count map, aggregate the found |
853 |
|
|
// data to *this symbol/count map. We'll have "merged" or "aggreated" the |
854 |
|
|
// other formula into *this one. |
855 |
|
|
|
856 |
|
✗ |
std::map<QString, double> map_copy = formula.getSymbolCountMap(); |
857 |
|
|
|
858 |
|
|
// qDebug() |
859 |
|
|
//<< "The external formula, after validation has a symbol/count map of size:" |
860 |
|
|
//<< map_copy.size(); |
861 |
|
|
|
862 |
|
✗ |
std::map<QString, double>::const_iterator iter = map_copy.cbegin(); |
863 |
|
✗ |
std::map<QString, double>::const_iterator iter_end = map_copy.cend(); |
864 |
|
|
|
865 |
|
✗ |
while(iter != iter_end) |
866 |
|
|
{ |
867 |
|
✗ |
accountSymbolCountPair(iter->first, iter->second * times); |
868 |
|
✗ |
++iter; |
869 |
|
|
} |
870 |
|
|
|
871 |
|
|
// qDebug() << "After having merged the external formula's map into this one, |
872 |
|
|
// " "this one has size:" |
873 |
|
|
//<< m_symbolCountMap.size(); |
874 |
|
|
|
875 |
|
|
// Update what's the text of the formula to represent what is in |
876 |
|
|
// atomCount list. |
877 |
|
✗ |
m_formula = elementalComposition(); |
878 |
|
|
|
879 |
|
|
// qDebug() << "And now this formula has text: " << m_formula; |
880 |
|
|
|
881 |
|
✗ |
return m_symbolCountMap.size(); |
882 |
|
✗ |
} |
883 |
|
|
|
884 |
|
|
|
885 |
|
|
#if 0 |
886 |
|
|
|
887 |
|
|
Old version that parsed the actionformula char by char. |
888 |
|
|
bool |
889 |
|
|
Formula::checkSyntax(const QString &formula, bool forceCountIndex) |
890 |
|
|
{ |
891 |
|
|
// Static function. |
892 |
|
|
|
893 |
|
|
// qDebug() << "Checking syntax with formula:" << formula; |
894 |
|
|
|
895 |
|
|
QChar curChar; |
896 |
|
|
|
897 |
|
|
bool gotUpper = false; |
898 |
|
|
bool wasSign = false; |
899 |
|
|
bool wasDigit = false; |
900 |
|
|
|
901 |
|
|
// Because the formula that we are analyzing might contain a title |
902 |
|
|
// and spaces , we first remove these. But make a local copy of |
903 |
|
|
// the member datum. |
904 |
|
|
|
905 |
|
|
QString localFormula = formula; |
906 |
|
|
|
907 |
|
|
// One formula can be like this: |
908 |
|
|
|
909 |
|
|
// "Decomposed adenine" C5H4N5 +H |
910 |
|
|
|
911 |
|
|
// The "Decomposed adenine" is the title |
912 |
|
|
// The C5H4N5 +H is the formula. |
913 |
|
|
|
914 |
|
|
localFormula.remove(QRegularExpression("\".*\"")); |
915 |
|
|
|
916 |
|
|
// We want to remove all the possibly-existing spaces. |
917 |
|
|
|
918 |
|
|
localFormula.remove(QRegularExpression("\\s+")); |
919 |
|
|
|
920 |
|
|
|
921 |
|
|
for(int iter = 0; iter < localFormula.length(); ++iter) |
922 |
|
|
{ |
923 |
|
|
curChar = localFormula.at(iter); |
924 |
|
|
|
925 |
|
|
// qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()" |
926 |
|
|
//<< "Current character:" << curChar; |
927 |
|
|
|
928 |
|
|
// FIXME One improvement that would ease modelling the Averagine would |
929 |
|
|
// be to silently allow double formula indices (that is, double atom |
930 |
|
|
// counts). They would not be compulsory |
931 |
|
|
|
932 |
|
|
if(curChar.category() == QChar::Number_DecimalDigit) |
933 |
|
|
{ |
934 |
|
|
// We are parsing a digit. |
935 |
|
|
|
936 |
|
|
// We may not have a digit after a +/- sign. |
937 |
|
|
if(wasSign) |
938 |
|
|
return false; |
939 |
|
|
|
940 |
|
|
wasSign = false; |
941 |
|
|
wasDigit = true; |
942 |
|
|
|
943 |
|
|
continue; |
944 |
|
|
} |
945 |
|
|
else if(curChar.category() == QChar::Letter_Lowercase) |
946 |
|
|
{ |
947 |
|
|
// Current character is lowercase, which means we are inside |
948 |
|
|
// of an atom symbol, such as Ca(the 'a') or Nob(either |
949 |
|
|
// 'o' or 'b'). Thus, gotUpper should be true ! |
950 |
|
|
|
951 |
|
|
if(!gotUpper) |
952 |
|
|
return false; |
953 |
|
|
|
954 |
|
|
// We may not have a lowercase character after a +/- sign. |
955 |
|
|
if(wasSign) |
956 |
|
|
return false; |
957 |
|
|
|
958 |
|
|
// Let the people know that we have parsed a lowercase char |
959 |
|
|
// and not a digit. |
960 |
|
|
wasSign = false; |
961 |
|
|
|
962 |
|
|
wasDigit = false; |
963 |
|
|
} |
964 |
|
|
else if(curChar.category() == QChar::Letter_Uppercase) |
965 |
|
|
{ |
966 |
|
|
// Current character is uppercase, which means that we are |
967 |
|
|
// at the beginning of an atom symbol. |
968 |
|
|
|
969 |
|
|
// There are two cases: |
970 |
|
|
// 1. We are starting for the very beginning of the formula, and |
971 |
|
|
// nothing came before this upper case character. That's fine. |
972 |
|
|
// 2. We had previously parsed a segment of the formula, and in this |
973 |
|
|
// case, we are closing a segment. If the parameter |
974 |
|
|
// obligatoryCountIndex is true, then we need to ensure that the |
975 |
|
|
// previous element had an associated number, even it the count |
976 |
|
|
// element is 1. This is required for the IsoSpec stuff in the gui |
977 |
|
|
// programs. |
978 |
|
|
|
979 |
|
|
if(iter > 0) |
980 |
|
|
{ |
981 |
|
|
if(forceCountIndex) |
982 |
|
|
{ |
983 |
|
|
if(!wasDigit) |
984 |
|
|
{ |
985 |
|
|
qDebug() |
986 |
|
|
<< "Returning false because upper case char was not" |
987 |
|
|
"preceded by digit while not at the first char of " |
988 |
|
|
"the formula"; |
989 |
|
|
|
990 |
|
|
return false; |
991 |
|
|
} |
992 |
|
|
} |
993 |
|
|
} |
994 |
|
|
|
995 |
|
|
// Let the people know what we got: |
996 |
|
|
|
997 |
|
|
wasSign = false; |
998 |
|
|
gotUpper = true; |
999 |
|
|
wasDigit = false; |
1000 |
|
|
} |
1001 |
|
|
else |
1002 |
|
|
{ |
1003 |
|
|
if(curChar != '+' && curChar != '-') |
1004 |
|
|
return false; |
1005 |
|
|
else |
1006 |
|
|
{ |
1007 |
|
|
// We may not have 2 +/- signs in a raw. |
1008 |
|
|
if(wasSign) |
1009 |
|
|
return false; |
1010 |
|
|
} |
1011 |
|
|
|
1012 |
|
|
wasSign = true; |
1013 |
|
|
gotUpper = false; |
1014 |
|
|
wasDigit = false; |
1015 |
|
|
} |
1016 |
|
|
} |
1017 |
|
|
// end for (int iter = 0 ; iter < localFormula.length() ; ++iter) |
1018 |
|
|
|
1019 |
|
|
// Note that if we want an obligatory count index, then, at the end of the |
1020 |
|
|
// formula, *compulsorily* we must have parsed a digit. |
1021 |
|
|
|
1022 |
|
|
if(forceCountIndex && !wasDigit) |
1023 |
|
|
{ |
1024 |
|
|
qDebug() |
1025 |
|
|
<< "Returning false because the formula does not end with a digit."; |
1026 |
|
|
|
1027 |
|
|
return false; |
1028 |
|
|
} |
1029 |
|
|
|
1030 |
|
|
// At this point we found no error condition. |
1031 |
|
|
return true; |
1032 |
|
|
} |
1033 |
|
|
#endif |
1034 |
|
|
|
1035 |
|
|
/*! |
1036 |
|
|
\brief Returns true if the member actionformula is syntactically valid, false |
1037 |
|
|
otherwise. |
1038 |
|
|
|
1039 |
|
|
\sa checkSyntax(const QString &formula, bool force_count_index) |
1040 |
|
|
*/ |
1041 |
|
|
bool |
1042 |
|
21708 |
Formula::checkSyntax() const |
1043 |
|
|
{ |
1044 |
|
|
// The default formula is always m_formula. |
1045 |
|
|
|
1046 |
|
21708 |
return checkSyntax(m_formula, m_forceCountIndex); |
1047 |
|
|
} |
1048 |
|
|
|
1049 |
|
|
/*! |
1050 |
|
|
\brief Returns true if the \a formula actionformula is syntactically |
1051 |
|
|
valid, false otherwise. |
1052 |
|
|
|
1053 |
|
|
If \a force_count_index is true, the syntax check accounts for the |
1054 |
|
|
requirement that all the symbols in the formula must be indexed, even if that |
1055 |
|
|
symbol's count is 1. This means that H2O would not pass the check, while H2O1 |
1056 |
|
|
would. |
1057 |
|
|
|
1058 |
|
|
The formula is first stripped of its title (if any), then all the spaces are |
1059 |
|
|
removed. |
1060 |
|
|
|
1061 |
|
|
MsXpS::libXpertMass::Formula::subFormulaRegExp is then used to extract each |
1062 |
|
|
"plus" and / or "minus" component while checking its syntactic validity. |
1063 |
|
|
|
1064 |
|
|
\note The syntax checking code does not verify that the actionformula is |
1065 |
|
|
chemically valid, that is, the "Cz4" symbol / count pair would check even if |
1066 |
|
|
the Cz chemical element does not exist. |
1067 |
|
|
|
1068 |
|
|
\sa validate() |
1069 |
|
|
*/ |
1070 |
|
|
bool |
1071 |
|
21708 |
Formula::checkSyntax(const QString &formula, bool force_count_index) |
1072 |
|
|
{ |
1073 |
|
|
// Because the formula that we are analyzing might contain a title |
1074 |
|
|
// and spaces , we first remove these. But make a local copy of |
1075 |
|
|
// the member datum. |
1076 |
|
|
|
1077 |
|
21708 |
QString localFormula = formula; |
1078 |
|
|
|
1079 |
|
|
// One formula can be like this: |
1080 |
|
|
|
1081 |
|
|
// "Decomposed adenine" C5H4N5 +H |
1082 |
|
|
|
1083 |
|
|
// The "Decomposed adenine" is the title |
1084 |
|
|
// The C5H4N5 +H is the formula. |
1085 |
|
|
|
1086 |
3/6
✓ Branch 1 taken 21708 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21708 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 21708 times.
✗ Branch 8 not taken.
|
21708 |
localFormula.remove(QRegularExpression("\".*\"")); |
1087 |
|
|
|
1088 |
|
|
// We want to remove all the possibly-existing spaces. |
1089 |
|
|
|
1090 |
3/6
✓ Branch 1 taken 21708 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21708 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 21708 times.
✗ Branch 8 not taken.
|
21708 |
localFormula.remove(QRegularExpression("\\s+")); |
1091 |
|
|
|
1092 |
|
|
// qDebug() << "The formula is:" << localFormula; |
1093 |
|
|
|
1094 |
|
|
// The raw formula might include: |
1095 |
|
|
// +/- sign before the symbol |
1096 |
|
|
// then the symbol (one uppercase any lowercase) |
1097 |
|
|
// then the count as an integer or a double. |
1098 |
|
|
|
1099 |
|
|
// Attention, the regular expression logic below works by finding |
1100 |
|
|
// patterns that match the regexp, BUT that does not means that |
1101 |
|
|
// spurious substrings at the beginning or at the end cannot be |
1102 |
|
|
// present and go undetected, like this: "3Cz3H12O6N14L2", where |
1103 |
|
|
// the '3' on the left is not seen. |
1104 |
|
|
|
1105 |
|
|
// We thus need to first ensure that the string never begins with |
1106 |
|
|
// something else than a +/- sign (optionally) and a letter (uppercase). |
1107 |
2/4
✓ Branch 1 taken 21708 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21708 times.
✗ Branch 5 not taken.
|
21708 |
QRegularExpression start_of_formula("^[+-]?[A-Z]"); |
1108 |
|
|
|
1109 |
1/2
✓ Branch 1 taken 21708 times.
✗ Branch 2 not taken.
|
21708 |
QRegularExpressionMatch match = start_of_formula.match(localFormula); |
1110 |
3/4
✓ Branch 1 taken 21708 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 28 times.
✓ Branch 4 taken 21680 times.
|
21708 |
if(!match.hasMatch()) |
1111 |
|
|
{ |
1112 |
2/4
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 28 times.
✗ Branch 6 not taken.
|
28 |
qDebug() << "Error at start of formula string."; |
1113 |
|
|
|
1114 |
|
28 |
return false; |
1115 |
|
|
} |
1116 |
|
|
|
1117 |
|
|
// Like wise for formulas that do not end either by [A-Z] or [a-z] or \\d. |
1118 |
2/4
✓ Branch 1 taken 21680 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21680 times.
✗ Branch 5 not taken.
|
21680 |
QRegularExpression end_of_formula("[A-Za-z\\d]$"); |
1119 |
1/2
✓ Branch 1 taken 21680 times.
✗ Branch 2 not taken.
|
21680 |
match = end_of_formula.match(localFormula); |
1120 |
3/4
✓ Branch 1 taken 21680 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 21676 times.
|
21680 |
if(!match.hasMatch()) |
1121 |
|
|
{ |
1122 |
2/4
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
|
4 |
qDebug() << "Error at end of formula string."; |
1123 |
|
|
|
1124 |
|
4 |
return false; |
1125 |
|
|
} |
1126 |
|
|
|
1127 |
|
21676 |
for(const QRegularExpressionMatch &match : |
1128 |
5/8
✓ Branch 1 taken 21676 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21676 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 67500 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 67508 times.
✓ Branch 13 taken 21668 times.
|
178352 |
subFormulaRegExp.globalMatch(localFormula)) |
1129 |
|
|
{ |
1130 |
1/2
✓ Branch 1 taken 67508 times.
✗ Branch 2 not taken.
|
67508 |
QString full_match = match.captured(0); |
1131 |
|
|
|
1132 |
|
|
// qDebug() << "The full sub-match:" << full_match; |
1133 |
|
|
|
1134 |
1/2
✓ Branch 1 taken 67508 times.
✗ Branch 2 not taken.
|
67508 |
QString sign = match.captured(1); |
1135 |
1/2
✓ Branch 1 taken 67508 times.
✗ Branch 2 not taken.
|
67508 |
QString symbol = match.captured(2); |
1136 |
1/2
✓ Branch 1 taken 67508 times.
✗ Branch 2 not taken.
|
67508 |
QString count_string = match.captured(3); |
1137 |
|
|
|
1138 |
2/2
✓ Branch 1 taken 66676 times.
✓ Branch 2 taken 832 times.
|
67508 |
if(!count_string.isEmpty()) |
1139 |
|
|
{ |
1140 |
|
66676 |
bool ok = false; |
1141 |
|
|
// Verify that it correctly converts to double. |
1142 |
1/2
✓ Branch 1 taken 66676 times.
✗ Branch 2 not taken.
|
66676 |
count_string.toDouble(&ok); |
1143 |
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 66676 times.
|
66676 |
if(!ok) |
1144 |
|
✗ |
return false; |
1145 |
|
|
} |
1146 |
|
|
else |
1147 |
|
|
{ |
1148 |
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 824 times.
|
832 |
if(force_count_index) |
1149 |
|
|
{ |
1150 |
4/8
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 8 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 8 times.
✗ Branch 12 not taken.
|
8 |
qDebug() << "Error: symbol" << symbol << "has no index."; |
1151 |
|
|
|
1152 |
|
8 |
return false; |
1153 |
|
|
} |
1154 |
|
|
else |
1155 |
|
|
{ |
1156 |
|
|
// qDebug() << "Symbol" << symbol |
1157 |
|
|
// << "has no index but that is tolerated."; |
1158 |
|
|
} |
1159 |
|
|
} |
1160 |
|
|
|
1161 |
|
|
// qDebug() << "Sign:" << match.captured(1) << "Symbol:" << |
1162 |
|
|
// match.captured(2) |
1163 |
|
|
// << "Count:" << match.captured(3); |
1164 |
12/12
✓ Branch 1 taken 67500 times.
✓ Branch 2 taken 8 times.
✓ Branch 4 taken 67500 times.
✓ Branch 5 taken 8 times.
✓ Branch 7 taken 67500 times.
✓ Branch 8 taken 8 times.
✓ Branch 10 taken 67500 times.
✓ Branch 11 taken 8 times.
✓ Branch 13 taken 21668 times.
✓ Branch 14 taken 8 times.
✓ Branch 16 taken 21668 times.
✓ Branch 17 taken 8 times.
|
89216 |
} |
1165 |
|
|
|
1166 |
|
|
// qDebug() << "Returning true"; |
1167 |
|
|
|
1168 |
|
21668 |
return true; |
1169 |
|
21708 |
} |
1170 |
|
|
|
1171 |
|
|
/*! Returns true if the formula validates successfully, false otherwise. |
1172 |
|
|
|
1173 |
|
|
The polymorphic function validate(IsotopicDataCstSPtr isotopic_data_csp, bool |
1174 |
|
|
store, bool reset) is called with both arguments set to false. |
1175 |
|
|
|
1176 |
|
|
The validation of this Formula instance is performed against the \a |
1177 |
|
|
isotopic_data_csp isotopic reference data. |
1178 |
|
|
*/ |
1179 |
|
|
bool |
1180 |
|
24772 |
Formula::validate(IsotopicDataCstSPtr isotopic_data_csp) |
1181 |
|
|
{ |
1182 |
1/2
✓ Branch 2 taken 24772 times.
✗ Branch 3 not taken.
|
24772 |
return validate(isotopic_data_csp, false, false); |
1183 |
|
|
} |
1184 |
|
|
|
1185 |
|
|
/*! Returns true if the formula validates successfully, false otherwise. |
1186 |
|
|
|
1187 |
|
|
The validation of the formula involves: |
1188 |
|
|
|
1189 |
|
|
\list |
1190 |
|
|
\li Checking that the member actionformula is not empty. Returns false |
1191 |
|
|
otherwise; |
1192 |
|
|
\li Splitting the actionformula into "plus" and "minus" components |
1193 |
|
|
(\l{splitActionParts}). If that steps fails, returns false; |
1194 |
|
|
\li Verifying that both the m_plusFormula and the m_minusFormula are not |
1195 |
|
|
empty. Returns false otherwise; |
1196 |
|
|
\endlist |
1197 |
|
|
|
1198 |
|
|
If \a store is true, the symbol / count data obtained while splitting the |
1199 |
|
|
"plus" and "minus" components of the actionformula are stored in the member |
1200 |
|
|
m_symbolCountMap map. |
1201 |
|
|
|
1202 |
|
|
If \a reset is true, the member symbol / count is first |
1203 |
|
|
reset. |
1204 |
|
|
|
1205 |
|
|
\a isotopic_data_csp are the isotopic data used as reference to ensure |
1206 |
|
|
chemical validity of the formula components. |
1207 |
|
|
*/ |
1208 |
|
|
bool |
1209 |
|
24792 |
Formula::validate(IsotopicDataCstSPtr isotopic_data_csp, bool store, bool reset) |
1210 |
|
|
{ |
1211 |
3/6
✓ Branch 1 taken 24792 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 24792 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 24792 times.
|
24792 |
if(isotopic_data_csp == nullptr || isotopic_data_csp.get() == nullptr) |
1212 |
|
✗ |
qFatal("Programming error. The isotopic data pointer cannot be nullptr."); |
1213 |
|
|
|
1214 |
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 24792 times.
|
24792 |
if(!isotopic_data_csp->size()) |
1215 |
|
✗ |
qFatal("Programming error. The isotopic data cannot be empty."); |
1216 |
|
|
|
1217 |
|
|
// qDebug() << "isotopic_data_csp.get():" << isotopic_data_csp.get(); |
1218 |
|
|
|
1219 |
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 24788 times.
|
24792 |
if(!m_formula.size()) |
1220 |
|
|
{ |
1221 |
2/4
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
|
4 |
qDebug() << "The formula is empty."; |
1222 |
|
4 |
return false; |
1223 |
|
|
} |
1224 |
|
|
|
1225 |
|
|
// qDebug() << "Now splitting formula" << m_formula << "into its action |
1226 |
|
|
// parts."; |
1227 |
|
|
|
1228 |
1/2
✓ Branch 2 taken 24788 times.
✗ Branch 3 not taken.
|
24788 |
int result = splitActionParts(isotopic_data_csp, 1, store, reset); |
1229 |
|
|
|
1230 |
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24788 times.
|
24788 |
if(result == static_cast<int>(FormulaSplitResult::FAILURE)) |
1231 |
|
|
{ |
1232 |
|
✗ |
qDebug() << "Failed splitting the formula into its action parts."; |
1233 |
|
✗ |
return false; |
1234 |
|
|
} |
1235 |
|
|
|
1236 |
|
|
// Both the action formulas cannot be empty. |
1237 |
4/6
✓ Branch 1 taken 6272 times.
✓ Branch 2 taken 18516 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 6272 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 24788 times.
|
24788 |
if(!m_plusFormula.size() && !m_minusFormula.size()) |
1238 |
|
|
{ |
1239 |
|
✗ |
qDebug() << "Both the plus and minus formulas are empty."; |
1240 |
|
✗ |
return false; |
1241 |
|
|
} |
1242 |
|
|
|
1243 |
|
|
// qDebug() << "Success: the formula validated fine."; |
1244 |
|
|
|
1245 |
|
24788 |
return true; |
1246 |
|
|
} |
1247 |
|
|
|
1248 |
|
|
/*! |
1249 |
|
|
\brief Accounts this formula's monoisotopic and average masses into \a mono |
1250 |
|
|
and \a avg, using \a times as a compounding factor. |
1251 |
|
|
|
1252 |
|
|
The masses corresponding to the member actionformula m_formula are |
1253 |
|
|
calculated first and then the \a mono and \a avg parameters are updated |
1254 |
|
|
by incrementing their value with the calculated values. This incrementation |
1255 |
|
|
might be compounded by that \a times factor. |
1256 |
|
|
|
1257 |
|
|
The masses of m_formula are computed using data from \a isotopic_data_csp. |
1258 |
|
|
|
1259 |
|
|
Returns true if no error was encountered, false otherwise. |
1260 |
|
|
|
1261 |
|
|
\sa splitActionParts() |
1262 |
|
|
*/ |
1263 |
|
|
bool |
1264 |
|
19460 |
Formula::accountMasses(IsotopicDataCstSPtr isotopic_data_csp, |
1265 |
|
|
double *mono, |
1266 |
|
|
double *avg, |
1267 |
|
|
double times) |
1268 |
|
|
{ |
1269 |
|
|
// Note the 'times' param below that ensures we create proper symbol/count |
1270 |
|
|
// map items by taking that compounding factor into account. |
1271 |
|
|
|
1272 |
|
|
// qDebug() << qSetRealNumberPrecision(6) |
1273 |
|
|
// << "We get two mono and avg variables with values:" << *mono << |
1274 |
|
|
// "-" |
1275 |
|
|
// << *avg << "and times:" << times; |
1276 |
|
|
|
1277 |
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 19460 times.
|
19460 |
if(isotopic_data_csp == nullptr) |
1278 |
|
✗ |
qFatal("Programming error. The pointer cannot be nullptr."); |
1279 |
|
|
|
1280 |
2/4
✓ Branch 2 taken 19460 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 19460 times.
|
38920 |
if(splitActionParts( |
1281 |
|
19460 |
isotopic_data_csp, times, true /* store */, true /* reset */) == |
1282 |
|
|
static_cast<int>(FormulaSplitResult::FAILURE)) |
1283 |
|
✗ |
return false; |
1284 |
|
|
|
1285 |
|
|
// qDebug() << "Formula::accountMasses:" |
1286 |
|
|
//<< "after splitActionParts:" |
1287 |
|
|
//<< "store: true ; reset: true" |
1288 |
|
|
//<< "m_formula:" << m_formula << "text" << Formula::toString(); |
1289 |
|
|
|
1290 |
|
|
// At this point m_symbolCountMap has all the symbol/count pairs needed to |
1291 |
|
|
// account for the masses. |
1292 |
|
|
|
1293 |
|
19460 |
std::map<QString, double>::const_iterator iter = m_symbolCountMap.cbegin(); |
1294 |
|
19460 |
std::map<QString, double>::const_iterator iter_end = m_symbolCountMap.cend(); |
1295 |
|
|
|
1296 |
|
|
// for(auto item : m_symbolCountMap) |
1297 |
|
|
// qDebug() << "One symbol count item:" << item.first << "/" << item.second; |
1298 |
|
|
|
1299 |
|
19460 |
bool ok = false; |
1300 |
|
|
|
1301 |
2/2
✓ Branch 1 taken 64764 times.
✓ Branch 2 taken 19460 times.
|
84224 |
while(iter != iter_end) |
1302 |
|
|
{ |
1303 |
|
64764 |
QString symbol = iter->first; |
1304 |
|
|
|
1305 |
|
|
// qDebug() << "Getting masses for symbol:" << symbol; |
1306 |
|
|
|
1307 |
1/2
✓ Branch 0 taken 64764 times.
✗ Branch 1 not taken.
|
64764 |
if(mono != nullptr) |
1308 |
|
|
{ |
1309 |
|
|
double mono_mass = |
1310 |
1/2
✓ Branch 3 taken 64764 times.
✗ Branch 4 not taken.
|
64764 |
isotopic_data_csp->getMonoMassBySymbol(iter->first, &ok); |
1311 |
|
|
|
1312 |
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 64764 times.
|
64764 |
if(!ok) |
1313 |
|
|
{ |
1314 |
|
✗ |
qDebug() << "Failed to get the mono mass."; |
1315 |
|
✗ |
return false; |
1316 |
|
|
} |
1317 |
|
|
|
1318 |
|
64764 |
*mono += mono_mass * iter->second; |
1319 |
|
|
} |
1320 |
|
|
|
1321 |
|
64764 |
ok = false; |
1322 |
|
|
|
1323 |
1/2
✓ Branch 0 taken 64764 times.
✗ Branch 1 not taken.
|
64764 |
if(avg != nullptr) |
1324 |
|
|
{ |
1325 |
|
|
double avg_mass = |
1326 |
1/2
✓ Branch 3 taken 64764 times.
✗ Branch 4 not taken.
|
64764 |
isotopic_data_csp->getAvgMassBySymbol(iter->first, &ok); |
1327 |
|
|
|
1328 |
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 64764 times.
|
64764 |
if(!ok) |
1329 |
|
✗ |
return false; |
1330 |
|
|
|
1331 |
|
64764 |
*avg += avg_mass * iter->second; |
1332 |
|
|
} |
1333 |
|
|
|
1334 |
|
64764 |
++iter; |
1335 |
1/2
✓ Branch 1 taken 64764 times.
✗ Branch 2 not taken.
|
64764 |
} |
1336 |
|
|
|
1337 |
|
19460 |
return true; |
1338 |
|
|
} |
1339 |
|
|
|
1340 |
|
|
|
1341 |
|
|
/*! |
1342 |
|
|
\brief Accounts this formula's monoisotopic and average masses into \a |
1343 |
|
|
ponderable, using \a times as a compounding factor. |
1344 |
|
|
|
1345 |
|
|
This function uses \l{accountMasses()}. |
1346 |
|
|
|
1347 |
|
|
\sa splitActionParts() |
1348 |
|
|
*/ |
1349 |
|
|
bool |
1350 |
|
8 |
Formula::accountMasses(IsotopicDataCstSPtr isotopic_data_csp, |
1351 |
|
|
Ponderable *ponderable, |
1352 |
|
|
double times) |
1353 |
|
|
{ |
1354 |
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 |
if(ponderable == nullptr) |
1355 |
|
✗ |
qFatal("Fatal error: pointer cannot be nullptr. Program aborted."); |
1356 |
|
|
|
1357 |
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
16 |
return accountMasses( |
1358 |
|
16 |
isotopic_data_csp, &ponderable->rmono(), &ponderable->ravg(), times); |
1359 |
|
|
|
1360 |
|
|
|
1361 |
|
|
//// Note the 'times' param below. |
1362 |
|
|
// if(splitActionParts(isotopic_data_csp, times, true, true) == |
1363 |
|
|
// FormulaSplitResult::FAILURE) |
1364 |
|
|
// return false; |
1365 |
|
|
|
1366 |
|
|
//// At this point m_symbolCountMap has all the symbol/count pairs needed to |
1367 |
|
|
//// account for the masses. |
1368 |
|
|
|
1369 |
|
|
// std::map<QString, int>::const_iterator iter = |
1370 |
|
|
// m_symbolCountMap.cbegin(); std::map<QString, int>::const_iterator iter_end |
1371 |
|
|
// = m_symbolCountMap.cend(); |
1372 |
|
|
|
1373 |
|
|
// while(iter != iter_end) |
1374 |
|
|
//{ |
1375 |
|
|
// double mono_mass = isotopic_data_csp->getMonoMassBySymbol(iter->first); |
1376 |
|
|
// ponderable->rmono() += mono_mass * iter->second; |
1377 |
|
|
|
1378 |
|
|
// double avg_mass = isotopic_data_csp->getAvgMassBySymbol(iter->first); |
1379 |
|
|
// ponderable->ravg() += avg_mass * iter->second; |
1380 |
|
|
|
1381 |
|
|
//++iter; |
1382 |
|
|
//} |
1383 |
|
|
|
1384 |
|
|
return true; |
1385 |
|
|
} |
1386 |
|
|
|
1387 |
|
|
|
1388 |
|
|
//! Account the atoms in \c this \c m_formula. |
1389 |
|
|
/*! |
1390 |
|
|
\brief Accounts this Formula's actionformula m_formula in the symbol / count |
1391 |
|
|
member m_symbolCountMap. |
1392 |
|
|
|
1393 |
|
|
Calls splitActionParts() to actually parse m_formula and account its |
1394 |
|
|
components to m_symbolCountMap. The accounting of the symbol / count can be |
1395 |
|
|
compounded by the \a times factor. |
1396 |
|
|
|
1397 |
|
|
While splitting the "plus" and "minus" components of the actionformula, their |
1398 |
|
|
validity is checked against the reference isotopic data \a isotopic_data_csp. |
1399 |
|
|
|
1400 |
|
|
This function is used when processively accounting many different formulas |
1401 |
|
|
into the symbol / count map. The formula is set to a new value and this |
1402 |
|
|
function is called without resetting the symbol / count map, effectively adding |
1403 |
|
|
formulas onto formulas sequentially. |
1404 |
|
|
|
1405 |
|
|
Returns true if no error was encountered, false otherwise. |
1406 |
|
|
|
1407 |
|
|
\sa splitActionParts(), Polymer::elementalComposition() |
1408 |
|
|
*/ |
1409 |
|
|
bool |
1410 |
|
✗ |
Formula::accountSymbolCounts(IsotopicDataCstSPtr isotopic_data_csp, int times) |
1411 |
|
|
{ |
1412 |
|
|
// Note the 'times' param below. |
1413 |
|
✗ |
if(splitActionParts(isotopic_data_csp, times, true, false) == |
1414 |
|
|
static_cast<int>(FormulaSplitResult::FAILURE)) |
1415 |
|
✗ |
return false; |
1416 |
|
|
|
1417 |
|
✗ |
return true; |
1418 |
|
|
} |
1419 |
|
|
|
1420 |
|
|
/*! |
1421 |
|
|
\brief Returns a formula matching the contents of the symbol / count member |
1422 |
|
|
map. |
1423 |
|
|
|
1424 |
|
|
The returned formula is formatted according to the IUPAC convention about the |
1425 |
|
|
ordering of the chemical elements: CxxHxxNxxOxxSxxPxx. |
1426 |
|
|
|
1427 |
|
|
The "plus" components are output first and the "minus" components after. |
1428 |
|
|
|
1429 |
|
|
If \a symbol_count_pairs_p is not nullptr, each symbol / count pair is added |
1430 |
|
|
to it. |
1431 |
|
|
*/ |
1432 |
|
|
QString |
1433 |
|
8 |
Formula::elementalComposition( |
1434 |
|
|
std::vector<std::pair<QString, double>> *symbol_count_pairs_p) const |
1435 |
|
|
{ |
1436 |
|
|
// Iterate in the symbol count member map and for each item output the symbol |
1437 |
|
|
// string accompanied by the corresponding count. Note that the count for any |
1438 |
|
|
// given symbol might be negative. We want to craft an elemental composition |
1439 |
|
|
// that accounts for "actions", that is a +elemental formula and a -elemental |
1440 |
|
|
// formula. |
1441 |
|
|
|
1442 |
|
8 |
std::map<QString, double>::const_iterator iter = m_symbolCountMap.cbegin(); |
1443 |
|
8 |
std::map<QString, double>::const_iterator iter_end = m_symbolCountMap.cend(); |
1444 |
|
|
|
1445 |
|
|
#if 0 |
1446 |
|
|
|
1447 |
|
|
qDebug() << "While computing the elemental composition corresponding to the " |
1448 |
|
|
"symbol/count map:"; |
1449 |
|
|
for(auto pair : m_symbolCountMap) |
1450 |
|
|
qDebug().noquote() << "(" << pair.first << "," << pair.second << ")"; |
1451 |
|
|
|
1452 |
|
|
#endif |
1453 |
|
|
|
1454 |
|
8 |
QStringList negativeStringList; |
1455 |
|
8 |
QStringList positiveStringList; |
1456 |
|
|
|
1457 |
2/2
✓ Branch 1 taken 40 times.
✓ Branch 2 taken 8 times.
|
48 |
while(iter != iter_end) |
1458 |
|
|
{ |
1459 |
|
40 |
QString symbol = iter->first; |
1460 |
|
40 |
double count = iter->second; |
1461 |
|
|
|
1462 |
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40 times.
|
40 |
if(count < 0) |
1463 |
|
|
{ |
1464 |
|
✗ |
negativeStringList.append( |
1465 |
|
✗ |
QString("%1%2").arg(symbol).arg(-1 * count)); |
1466 |
|
|
} |
1467 |
|
|
else |
1468 |
|
|
{ |
1469 |
4/8
✓ Branch 1 taken 40 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 40 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 40 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 40 times.
✗ Branch 11 not taken.
|
120 |
positiveStringList.append(QString("%1%2").arg(symbol).arg(count)); |
1470 |
|
|
} |
1471 |
|
|
|
1472 |
|
40 |
++iter; |
1473 |
|
40 |
} |
1474 |
|
|
|
1475 |
|
|
// We want to provide a formula that lists the positive component |
1476 |
|
|
// first and the negative component last. |
1477 |
|
|
|
1478 |
|
|
// Each positive/negative component will list the atoms in the |
1479 |
|
|
// conventional order : CxxHxxNxxOxx and all the rest in |
1480 |
|
|
// alphabetical order. |
1481 |
|
|
|
1482 |
|
|
// We want to provide for each positive and negative components of the |
1483 |
|
|
// initial formula object, an elemental formula that complies with the |
1484 |
|
|
// convention : first the C atom, next the H, N, O, S, P atoms and all the |
1485 |
|
|
// subsequent ones in alphabetical order. |
1486 |
|
|
|
1487 |
|
|
// Sort the lists. |
1488 |
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 |
negativeStringList.sort(); |
1489 |
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 |
positiveStringList.sort(); |
1490 |
|
|
|
1491 |
|
|
// Thus we look for the four C, H, N, O, S,P atoms, and we create the |
1492 |
|
|
// initial part of the elemental formula. Each time we find one |
1493 |
|
|
// such atom we remove it from the list, so that we can later just |
1494 |
|
|
// append all the remaining atoms, since we have sorted the lists |
1495 |
|
|
// above. |
1496 |
|
|
|
1497 |
|
|
// The positive component |
1498 |
|
|
// ====================== |
1499 |
|
|
|
1500 |
|
8 |
int symbol_index_in_list = 0; |
1501 |
|
8 |
QString positiveComponentString; |
1502 |
|
|
|
1503 |
|
|
// Carbon |
1504 |
|
8 |
symbol_index_in_list = |
1505 |
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
|
8 |
positiveStringList.indexOf(QRegularExpression("C\\d*[\\.]?\\d*")); |
1506 |
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 |
if(symbol_index_in_list != -1) |
1507 |
|
|
{ |
1508 |
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 |
positiveComponentString += positiveStringList.at(symbol_index_in_list); |
1509 |
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 |
positiveStringList.removeAt(symbol_index_in_list); |
1510 |
|
|
|
1511 |
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 |
if(symbol_count_pairs_p) |
1512 |
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 |
symbol_count_pairs_p->push_back( |
1513 |
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
|
16 |
std::pair<QString, double>("C", m_symbolCountMap.at("C"))); |
1514 |
|
|
} |
1515 |
|
|
|
1516 |
|
|
// Hydrogen |
1517 |
|
8 |
symbol_index_in_list = |
1518 |
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
|
8 |
positiveStringList.indexOf(QRegularExpression("H\\d*[\\.]?\\d*")); |
1519 |
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 |
if(symbol_index_in_list != -1) |
1520 |
|
|
{ |
1521 |
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 |
positiveComponentString += positiveStringList.at(symbol_index_in_list); |
1522 |
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 |
positiveStringList.removeAt(symbol_index_in_list); |
1523 |
|
|
|
1524 |
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 |
if(symbol_count_pairs_p) |
1525 |
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 |
symbol_count_pairs_p->push_back( |
1526 |
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
|
16 |
std::pair<QString, double>("H", m_symbolCountMap.at("H"))); |
1527 |
|
|
} |
1528 |
|
|
|
1529 |
|
|
// Nitrogen |
1530 |
|
8 |
symbol_index_in_list = |
1531 |
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
|
8 |
positiveStringList.indexOf(QRegularExpression("N\\d*[\\.]?\\d*")); |
1532 |
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 |
if(symbol_index_in_list != -1) |
1533 |
|
|
{ |
1534 |
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 |
positiveComponentString += positiveStringList.at(symbol_index_in_list); |
1535 |
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 |
positiveStringList.removeAt(symbol_index_in_list); |
1536 |
|
|
|
1537 |
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 |
if(symbol_count_pairs_p) |
1538 |
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 |
symbol_count_pairs_p->push_back( |
1539 |
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
|
16 |
std::pair<QString, double>("N", m_symbolCountMap.at("N"))); |
1540 |
|
|
} |
1541 |
|
|
|
1542 |
|
|
// Oxygen |
1543 |
|
8 |
symbol_index_in_list = |
1544 |
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
|
8 |
positiveStringList.indexOf(QRegularExpression("O\\d*[\\.]?\\d*")); |
1545 |
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 |
if(symbol_index_in_list != -1) |
1546 |
|
|
{ |
1547 |
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 |
positiveComponentString += positiveStringList.at(symbol_index_in_list); |
1548 |
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 |
positiveStringList.removeAt(symbol_index_in_list); |
1549 |
|
|
|
1550 |
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 |
if(symbol_count_pairs_p) |
1551 |
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 |
symbol_count_pairs_p->push_back( |
1552 |
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
|
16 |
std::pair<QString, double>("O", m_symbolCountMap.at("O"))); |
1553 |
|
|
} |
1554 |
|
|
|
1555 |
|
|
// Sulfur |
1556 |
|
8 |
symbol_index_in_list = |
1557 |
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
|
8 |
positiveStringList.indexOf(QRegularExpression("S\\d*[\\.]?\\d*")); |
1558 |
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 |
if(symbol_index_in_list != -1) |
1559 |
|
|
{ |
1560 |
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 |
positiveComponentString += positiveStringList.at(symbol_index_in_list); |
1561 |
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 |
positiveStringList.removeAt(symbol_index_in_list); |
1562 |
|
|
|
1563 |
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 |
if(symbol_count_pairs_p) |
1564 |
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 |
symbol_count_pairs_p->push_back( |
1565 |
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
|
16 |
std::pair<QString, double>("S", m_symbolCountMap.at("S"))); |
1566 |
|
|
} |
1567 |
|
|
|
1568 |
|
|
// Phosphorus |
1569 |
|
8 |
symbol_index_in_list = |
1570 |
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
|
8 |
positiveStringList.indexOf(QRegularExpression("P\\d*[\\.]?\\d*")); |
1571 |
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 |
if(symbol_index_in_list != -1) |
1572 |
|
|
{ |
1573 |
|
✗ |
positiveComponentString += positiveStringList.at(symbol_index_in_list); |
1574 |
|
✗ |
positiveStringList.removeAt(symbol_index_in_list); |
1575 |
|
|
|
1576 |
|
✗ |
if(symbol_count_pairs_p) |
1577 |
|
✗ |
symbol_count_pairs_p->push_back( |
1578 |
|
✗ |
std::pair<QString, double>("P", m_symbolCountMap.at("P"))); |
1579 |
|
|
} |
1580 |
|
|
|
1581 |
|
|
// Go on with all the other ones, if any... |
1582 |
|
|
|
1583 |
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
|
8 |
for(int iter = 0; iter < positiveStringList.size(); ++iter) |
1584 |
|
|
{ |
1585 |
|
✗ |
positiveComponentString += positiveStringList.at(iter); |
1586 |
|
|
|
1587 |
|
✗ |
QRegularExpression regexp("([A-Z][a-z]*)(\\d*[\\.]?\\d*)"); |
1588 |
|
✗ |
QRegularExpressionMatch match = regexp.match(positiveStringList.at(iter)); |
1589 |
|
|
|
1590 |
|
✗ |
if(match.hasMatch()) |
1591 |
|
|
{ |
1592 |
|
✗ |
QString symbol = match.captured(1); |
1593 |
|
✗ |
QString howMany = match.captured(2); |
1594 |
|
|
|
1595 |
|
✗ |
bool ok = false; |
1596 |
|
✗ |
double count = howMany.toDouble(&ok); |
1597 |
|
|
|
1598 |
|
✗ |
if(!count && !ok) |
1599 |
|
✗ |
qFatal( |
1600 |
|
|
"Fatal error at %s@%d -- %s(). " |
1601 |
|
|
"Failed to parse an atom count." |
1602 |
|
|
"Program aborted.", |
1603 |
|
|
__FILE__, |
1604 |
|
|
__LINE__, |
1605 |
|
|
__FUNCTION__); |
1606 |
|
|
|
1607 |
|
✗ |
if(symbol_count_pairs_p) |
1608 |
|
✗ |
symbol_count_pairs_p->push_back( |
1609 |
|
✗ |
std::pair<QString, double>(symbol, count)); |
1610 |
|
✗ |
} |
1611 |
|
✗ |
} |
1612 |
|
|
|
1613 |
|
|
// qDebug() << __FILE__ << __LINE__ |
1614 |
|
|
//<< "positiveComponentString:" << positiveComponentString; |
1615 |
|
|
|
1616 |
|
|
|
1617 |
|
|
// The negative component |
1618 |
|
|
// ====================== |
1619 |
|
|
|
1620 |
|
8 |
symbol_index_in_list = 0; |
1621 |
|
8 |
QString negativeComponentString; |
1622 |
|
|
|
1623 |
|
|
// Carbon |
1624 |
|
8 |
symbol_index_in_list = |
1625 |
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
|
8 |
negativeStringList.indexOf(QRegularExpression("C\\d*[\\.]?\\d*")); |
1626 |
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 |
if(symbol_index_in_list != -1) |
1627 |
|
|
{ |
1628 |
|
✗ |
negativeComponentString += negativeStringList.at(symbol_index_in_list); |
1629 |
|
✗ |
negativeStringList.removeAt(symbol_index_in_list); |
1630 |
|
|
|
1631 |
|
✗ |
if(symbol_count_pairs_p) |
1632 |
|
✗ |
symbol_count_pairs_p->push_back( |
1633 |
|
✗ |
std::pair<QString, double>("C", m_symbolCountMap.at("C"))); |
1634 |
|
|
} |
1635 |
|
|
|
1636 |
|
|
// Hydrogen |
1637 |
|
8 |
symbol_index_in_list = |
1638 |
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
|
8 |
negativeStringList.indexOf(QRegularExpression("H\\d*[\\.]?\\d*")); |
1639 |
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 |
if(symbol_index_in_list != -1) |
1640 |
|
|
{ |
1641 |
|
✗ |
negativeComponentString += negativeStringList.at(symbol_index_in_list); |
1642 |
|
✗ |
negativeStringList.removeAt(symbol_index_in_list); |
1643 |
|
|
|
1644 |
|
✗ |
if(symbol_count_pairs_p) |
1645 |
|
✗ |
symbol_count_pairs_p->push_back( |
1646 |
|
✗ |
std::pair<QString, double>("H", m_symbolCountMap.at("H"))); |
1647 |
|
|
} |
1648 |
|
|
|
1649 |
|
|
// Nitrogen |
1650 |
|
8 |
symbol_index_in_list = |
1651 |
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
|
8 |
negativeStringList.indexOf(QRegularExpression("N\\d*[\\.]?\\d*")); |
1652 |
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 |
if(symbol_index_in_list != -1) |
1653 |
|
|
{ |
1654 |
|
✗ |
negativeComponentString += negativeStringList.at(symbol_index_in_list); |
1655 |
|
✗ |
negativeStringList.removeAt(symbol_index_in_list); |
1656 |
|
|
|
1657 |
|
✗ |
if(symbol_count_pairs_p) |
1658 |
|
✗ |
symbol_count_pairs_p->push_back( |
1659 |
|
✗ |
std::pair<QString, double>("N", m_symbolCountMap.at("N"))); |
1660 |
|
|
} |
1661 |
|
|
|
1662 |
|
|
// Oxygen |
1663 |
|
8 |
symbol_index_in_list = |
1664 |
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
|
8 |
negativeStringList.indexOf(QRegularExpression("O\\d*[\\.]?\\d*")); |
1665 |
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 |
if(symbol_index_in_list != -1) |
1666 |
|
|
{ |
1667 |
|
✗ |
negativeComponentString += negativeStringList.at(symbol_index_in_list); |
1668 |
|
✗ |
negativeStringList.removeAt(symbol_index_in_list); |
1669 |
|
|
|
1670 |
|
✗ |
if(symbol_count_pairs_p) |
1671 |
|
✗ |
symbol_count_pairs_p->push_back( |
1672 |
|
✗ |
std::pair<QString, double>("O", m_symbolCountMap.at("O"))); |
1673 |
|
|
} |
1674 |
|
|
|
1675 |
|
|
// Sulfur |
1676 |
|
8 |
symbol_index_in_list = |
1677 |
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
|
8 |
negativeStringList.indexOf(QRegularExpression("S\\d*[\\.]?\\d*")); |
1678 |
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 |
if(symbol_index_in_list != -1) |
1679 |
|
|
{ |
1680 |
|
✗ |
negativeComponentString += negativeStringList.at(symbol_index_in_list); |
1681 |
|
✗ |
negativeStringList.removeAt(symbol_index_in_list); |
1682 |
|
|
|
1683 |
|
✗ |
if(symbol_count_pairs_p) |
1684 |
|
✗ |
symbol_count_pairs_p->push_back( |
1685 |
|
✗ |
std::pair<QString, double>("S", m_symbolCountMap.at("S"))); |
1686 |
|
|
} |
1687 |
|
|
|
1688 |
|
|
// Phosphorus |
1689 |
|
8 |
symbol_index_in_list = |
1690 |
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
|
8 |
negativeStringList.indexOf(QRegularExpression("P\\d*[\\.]?\\d*")); |
1691 |
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 |
if(symbol_index_in_list != -1) |
1692 |
|
|
{ |
1693 |
|
✗ |
negativeComponentString += negativeStringList.at(symbol_index_in_list); |
1694 |
|
✗ |
negativeStringList.removeAt(symbol_index_in_list); |
1695 |
|
|
|
1696 |
|
✗ |
if(symbol_count_pairs_p) |
1697 |
|
✗ |
symbol_count_pairs_p->push_back( |
1698 |
|
✗ |
std::pair<QString, double>("P", m_symbolCountMap.at("P"))); |
1699 |
|
|
} |
1700 |
|
|
|
1701 |
|
|
// Go on with all the other ones, if any... |
1702 |
|
|
|
1703 |
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
|
8 |
for(int iter = 0; iter < negativeStringList.size(); ++iter) |
1704 |
|
|
{ |
1705 |
|
✗ |
negativeComponentString += negativeStringList.at(iter); |
1706 |
|
|
|
1707 |
|
✗ |
QRegularExpression regexp("([A-Z][a-z]*)(\\d*[\\.]?\\d*)"); |
1708 |
|
✗ |
QRegularExpressionMatch match = regexp.match(negativeStringList.at(iter)); |
1709 |
|
|
|
1710 |
|
✗ |
if(match.hasMatch()) |
1711 |
|
|
{ |
1712 |
|
✗ |
QString symbol = match.captured(1); |
1713 |
|
✗ |
QString howMany = match.captured(2); |
1714 |
|
|
|
1715 |
|
✗ |
bool ok = false; |
1716 |
|
✗ |
double count = howMany.toInt(&ok, 10); |
1717 |
|
|
|
1718 |
|
✗ |
if(!count && !ok) |
1719 |
|
✗ |
qFatal( |
1720 |
|
|
"Fatal error at %s@%d -- %s(). " |
1721 |
|
|
"Failed to parse an atom count." |
1722 |
|
|
"Program aborted.", |
1723 |
|
|
__FILE__, |
1724 |
|
|
__LINE__, |
1725 |
|
|
__FUNCTION__); |
1726 |
|
|
|
1727 |
|
✗ |
if(symbol_count_pairs_p) |
1728 |
|
✗ |
symbol_count_pairs_p->push_back( |
1729 |
|
✗ |
std::pair<QString, double>(symbol, count)); |
1730 |
|
✗ |
} |
1731 |
|
✗ |
} |
1732 |
|
|
|
1733 |
|
|
|
1734 |
|
|
// qDebug() << __FILE__ << __LINE__ |
1735 |
|
|
//<< "negativeComponentString:" << negativeComponentString; |
1736 |
|
|
|
1737 |
|
|
// Create the final elemental formula that comprises both the |
1738 |
|
|
// positive and negative element. First the positive element and |
1739 |
|
|
// then the negative one. Only append the negative one, prepended |
1740 |
|
|
// with '-' if the string is non-empty. |
1741 |
|
|
|
1742 |
|
8 |
QString elementalComposition = positiveComponentString; |
1743 |
|
|
|
1744 |
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
|
8 |
if(!negativeComponentString.isEmpty()) |
1745 |
|
✗ |
elementalComposition += QString("-%1").arg(negativeComponentString); |
1746 |
|
|
|
1747 |
|
|
// qDebug() << __FILE__ << __LINE__ |
1748 |
|
|
// <<"elementalComposition:" << elementalComposition; |
1749 |
|
|
|
1750 |
|
8 |
return elementalComposition; |
1751 |
|
8 |
} |
1752 |
|
|
|
1753 |
|
|
|
1754 |
|
|
/*! |
1755 |
|
|
\brief Returns the total count of symbols (atoms) in this formula. |
1756 |
|
|
|
1757 |
|
|
The determination is performed by summing up all the count values for all the |
1758 |
|
|
symbols in the member symbol / count pairs in the member map m_symbolCountMap. |
1759 |
|
|
*/ |
1760 |
|
|
double |
1761 |
|
✗ |
Formula::totalAtoms() const |
1762 |
|
|
{ |
1763 |
|
|
|
1764 |
|
✗ |
double total_atom_count = 0; |
1765 |
|
|
|
1766 |
|
✗ |
std::map<QString, double>::const_iterator iter = m_symbolCountMap.cbegin(); |
1767 |
|
✗ |
std::map<QString, double>::const_iterator iter_end = m_symbolCountMap.cend(); |
1768 |
|
|
|
1769 |
|
✗ |
while(iter != iter_end) |
1770 |
|
|
{ |
1771 |
|
✗ |
total_atom_count += iter->second; |
1772 |
|
✗ |
++iter; |
1773 |
|
|
} |
1774 |
|
|
|
1775 |
|
✗ |
return total_atom_count; |
1776 |
|
|
} |
1777 |
|
|
|
1778 |
|
|
|
1779 |
|
|
/*! |
1780 |
|
|
\brief Returns the total count of isotopes in this formula using \a |
1781 |
|
|
isotopic_data_csp as the reference isotopic data. |
1782 |
|
|
|
1783 |
|
|
The determination is performed by summing up all the isotope counts for |
1784 |
|
|
all the symbols keys in the member symbol / count map m_symbolCountMap. |
1785 |
|
|
*/ |
1786 |
|
|
double |
1787 |
|
✗ |
Formula::totalIsotopes(IsotopicDataCstSPtr isotopic_data_csp) const |
1788 |
|
|
{ |
1789 |
|
✗ |
double total_isotope_count = 0; |
1790 |
|
|
|
1791 |
|
✗ |
std::map<QString, double>::const_iterator iter = m_symbolCountMap.cbegin(); |
1792 |
|
✗ |
std::map<QString, double>::const_iterator iter_end = m_symbolCountMap.cend(); |
1793 |
|
|
|
1794 |
|
✗ |
while(iter != iter_end) |
1795 |
|
|
{ |
1796 |
|
✗ |
total_isotope_count += |
1797 |
|
✗ |
iter->second * isotopic_data_csp->getIsotopeCountBySymbol(iter->first); |
1798 |
|
|
|
1799 |
|
✗ |
++iter; |
1800 |
|
|
} |
1801 |
|
|
|
1802 |
|
✗ |
return total_isotope_count; |
1803 |
|
|
} |
1804 |
|
|
|
1805 |
|
|
/*! |
1806 |
|
|
\brief Returns the count value associated with key \a symbol in the symbol / |
1807 |
|
|
count member map m_symbolCountMap. |
1808 |
|
|
*/ |
1809 |
|
|
double |
1810 |
|
✗ |
Formula::symbolCount(const QString &symbol) const |
1811 |
|
|
{ |
1812 |
|
|
// Return the symbol index. |
1813 |
|
|
|
1814 |
|
✗ |
std::map<QString, double>::const_iterator iter_end = m_symbolCountMap.cend(); |
1815 |
|
|
|
1816 |
|
|
std::map<QString, double>::const_iterator iter = |
1817 |
|
✗ |
m_symbolCountMap.find(symbol); |
1818 |
|
|
|
1819 |
|
✗ |
if(iter == iter_end) |
1820 |
|
✗ |
return 0; |
1821 |
|
|
|
1822 |
|
✗ |
return iter->second; |
1823 |
|
|
} |
1824 |
|
|
|
1825 |
|
|
/*! |
1826 |
|
|
\brief Returns true if the member "minus" formula component is not empty, |
1827 |
|
|
false otherwise. |
1828 |
|
|
*/ |
1829 |
|
|
bool |
1830 |
|
✗ |
Formula::hasNetMinusPart() |
1831 |
|
|
{ |
1832 |
|
✗ |
return m_minusFormula.size(); |
1833 |
|
|
} |
1834 |
|
|
|
1835 |
|
|
|
1836 |
|
|
/*! |
1837 |
|
|
\brief Parses a formula XML \a element, sets the data to the |
1838 |
|
|
member actionformula m_formula and checks it syntax. |
1839 |
|
|
|
1840 |
|
|
Returns true if parsing and syntax checking were successful, false |
1841 |
|
|
otherwise. |
1842 |
|
|
|
1843 |
|
|
\sa checkSyntax() |
1844 |
|
|
*/ |
1845 |
|
|
bool |
1846 |
|
21564 |
Formula::renderXmlFormulaElement(const QDomElement &element) |
1847 |
|
|
{ |
1848 |
2/4
✓ Branch 1 taken 21564 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 21564 times.
|
21564 |
if(element.tagName() != "formula") |
1849 |
|
✗ |
return false; |
1850 |
|
|
|
1851 |
1/2
✓ Branch 1 taken 21564 times.
✗ Branch 2 not taken.
|
21564 |
m_formula = element.text(); |
1852 |
|
|
|
1853 |
|
|
// qDebug() << "Rendering formula element with text:" << m_formula; |
1854 |
|
|
|
1855 |
|
|
// Do not forget that we might have a title associated with the |
1856 |
|
|
// formula and spaces. checkSyntax() should care of removing these |
1857 |
|
|
// title and spaces before checking for chemical syntax |
1858 |
|
|
// correctness. |
1859 |
|
|
|
1860 |
|
21564 |
return checkSyntax(); |
1861 |
|
|
} |
1862 |
|
|
|
1863 |
|
|
} // namespace libXpertMass |
1864 |
|
|
|
1865 |
|
|
} // namespace MsXpS |
1866 |
|
|
|