GCC Code Coverage Report


./
File: src/XpertMass/Formula.cpp
Date: 2024-08-24 11:26:06
Lines:
260/421
61.8%
Functions:
23/38
60.5%
Branches:
258/688
37.5%

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