GCC Code Coverage Report


./
File: src/XpertMass/globals.cpp
Date: 2024-08-24 11:26:06
Lines:
30/190
15.8%
Functions:
4/21
19.0%
Branches:
29/212
13.7%

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 /////////////////////// StdLib includes
35 #include <math.h>
36
37
38 /////////////////////// Qt includes
39 #include <QDir>
40 #include <QString>
41 #include <QStandardPaths>
42 #include <QFile>
43 #include <QTextStream>
44
45
46 /////////////////////// pappsomspp includes
47
48
49 /////////////////////// Local includes
50 #include "globals.hpp"
51
52
53 namespace MsXpS
54 {
55
56 namespace libXpertMass
57 {
58
59
60 void
61 24 myMessageOutputFormat(QtMsgType type,
62 const QMessageLogContext &context,
63 const QString &msg)
64 {
65
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 QByteArray localMsg = msg.toLocal8Bit();
66
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 const char *file = context.file ? context.file : "";
67
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 const char *function = context.function ? context.function : "";
68
1/6
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
24 switch(type)
69 {
70 24 case QtDebugMsg:
71 24 fprintf(stderr,
72 "Debug: %s@%u -- %s() %s\n",
73 file,
74
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 context.line,
75 function,
76 localMsg.constData());
77 24 break;
78 case QtInfoMsg:
79 fprintf(stderr,
80 "Info: %s (%s:%u, %s)\n",
81 localMsg.constData(),
82 file,
83 context.line,
84 function);
85 break;
86 case QtWarningMsg:
87 fprintf(stderr,
88 "Warning: %s (%s:%u, %s)\n",
89 localMsg.constData(),
90 file,
91 context.line,
92 function);
93 break;
94 case QtCriticalMsg:
95 fprintf(stderr,
96 "Critical: %s (%s:%u, %s)\n",
97 localMsg.constData(),
98 file,
99 context.line,
100 function);
101 break;
102 case QtFatalMsg:
103 fprintf(stderr,
104 "Fatal: %s (%s:%u, %s)\n",
105 localMsg.constData(),
106 file,
107 context.line,
108 function);
109 break;
110 }
111 24 }
112
113 void
114 172 configureDebugMessagesFormat()
115 {
116
117 // Set the debugging message formatting pattern.
118 // qSetMessagePattern(QString("Debug: %{file}@%{line}-%{function}():
119 // %{message}"));
120 172 qInstallMessageHandler(myMessageOutputFormat);
121 172 }
122
123 /*!
124 \enum MsXpS::libXpertMass::MassType
125 \ingroup Globals
126
127 This enum type specifies the type of mass:
128
129 \value MASS_NONE
130 The mass type is not defined
131 \value MASS_MONO
132 The mass is monoisotopic
133 \value MASS_AVG
134 The mass is average
135 \value MASS_BOTH
136 (MASS_MONO | MASS_AVG)
137 */
138
139 /*!
140 \enum MsXpS::libXpertMass::MassToleranceType
141 \ingroup Globals
142
143 This enum type specifies the kind of mass tolerance to use for a mass
144 calculation or a mass comparison.
145
146 \value MASS_TOLERANCE_NONE
147 The tolerance is not specified
148 \value MASS_TOLERANCE_PPM
149 The tolerance is based on parts per million
150 \value MASS_TOLERANCE_MZ
151 The tolerance is based on an absolute m/z value
152 \value MASS_TOLERANCE_AMU
153 The tolerance is based on an absolute mass value
154 \value MASS_TOLERANCE_RES
155 The tolerance is based on resolution
156 \omitvalue MASS_TOLERANCE_LAST
157 */
158
159 /*!
160 \brief Map relating the \l{MassToleranceType} to a textual representation
161 \ingroup Globals
162 */
163 QMap<int, QString> massToleranceTypeMap{
164 {MassToleranceType::MASS_TOLERANCE_NONE, "NONE"},
165 {MassToleranceType::MASS_TOLERANCE_PPM, "PPM"},
166 {MassToleranceType::MASS_TOLERANCE_MZ, "MZ"},
167 {MassToleranceType::MASS_TOLERANCE_AMU, "AMU"},
168 {MassToleranceType::MASS_TOLERANCE_RES, "RES"}};
169
170
171 /*!
172 \enum MsXpS::libXpertMass::PolymerEnd
173 \ingroup Globals
174
175 This enum specifies the polymer end.
176
177 \value END_NONE
178 Not defined
179
180 \value END_LEFT
181 The left end
182
183 \value END_RIGHT
184 The right end
185
186 \value END_BOTH
187 (END_LEFT | END_RIGHT)
188 */
189
190 /*!
191 \enum MsXpS::libXpertMass::SelectionType
192 \ingroup Globals
193
194 This enum specifies the selection type in a polymer sequence.
195
196 \value SELECTION_TYPE_RESIDUAL_CHAINS
197 The selection comprises only residues
198
199 \value SELECTION_TYPE_OLIGOMERS
200 The selection comprises oligomers, that is, residual chains capped with
201 the left and right caps.
202 */
203
204 /*!
205 \enum MsXpS::libXpertMass::CapType
206 \ingroup Globals
207
208 This enum specifies the type of cap (the chemical entities that are set to
209 the polymer ends so as to finish its polymerization state from a chain of
210 residues to an actual polymer molecule.
211
212 \value CAP_NONE
213 The cap is not defined
214
215 \value CAP_LEFT
216 The left cap
217
218 \value CAP_RIGHT
219 The right cap
220
221 \value CAP_BOTH
222 (CAP_LEFT | CAP_RIGHT)
223 */
224
225 /*!
226 \enum MsXpS::libXpertMass::MonomerChemEnt
227 \ingroup Globals
228
229 This enum specifies the monomer chemical entities to account for in a
230 calculation.
231
232 This enum is typically used when mass calculations need to account or not for
233 the various chemical entities that are attached to a given monomer.
234
235 \value MONOMER_CHEMENT_NONE
236 The monomer chemical entity is not defined.
237
238 \value MONOMER_CHEMENT_MODIF
239 The monomer modifications
240
241 \value MONOMER_CHEMENT_CROSS_LINK
242 The monomer cross-links
243 */
244
245 /*!
246 \enum MsXpS::libXpertMass::PolymerChemEnt
247 \ingroup Globals
248
249 This enum specifies the polymer sequence chemical entities to account for in a
250 calculation.
251
252
253 This enum is typically used when mass calculations need to account or not for
254 the various chemical entities that are attached to a given polymer.
255
256 \value POLYMER_CHEMENT_NONE
257 The polymer chemical entity is not defined.
258
259 \value POLYMER_CHEMENT_LEFT_END_MODIF
260 The left end modification
261
262 \value POLYMER_CHEMENT_FORCE_LEFT_END_MODIF
263 The left end modification, even if that polymer's end is not selected
264
265
266 \value POLYMER_CHEMENT_RIGHT_END_MODIF
267 The right end modification
268
269 \value POLYMER_CHEMENT_FORCE_RIGHT_END_MODIF
270 The right end modification, even if that polymer's end is not
271 selected
272
273 \value POLYMER_CHEMENT_BOTH_END_MODIF
274 (POLYMER_CHEMENT_LEFT_END_MODIF | POLYMER_CHEMENT_RIGHT_END_MODIF)
275
276 \value POLYMER_CHEMENT_FORCE_BOTH_END_MODIF
277 (POLYMER_CHEMENT_FORCE_LEFT_END_MODIF |
278 POLYMER_CHEMENT_FORCE_RIGHT_END_MODIF)
279 */
280
281
282 /*!
283 \enum MsXpS::libXpertMass::HashAccountData
284 \ingroup Globals
285
286 This enum specifies the chemical entites to account for when calculating a hash.
287
288 \value HASH_ACCOUNT_SEQUENCE
289 The sequence
290
291 \value HASH_ACCOUNT_MONOMER_MODIF
292 Monomer modifications
293
294 \value HASH_ACCOUNT_POLYMER_MODIF
295 Polymer modifications
296 */
297
298
299 /*!
300 \variable MsXpS::libXpertMass::subFormulaRegExp
301
302 \brief Regular expression used to deconstruct the main formula into
303 minus and plus component subformulas.
304
305 \code
306 "([+-]?)([A-Z][a-z]*)(\\d*[\\.]?\\d*)"
307 \endcode
308
309 \sa Formula::splitActionParts()
310 */
311 QRegularExpression subFormulaRegExp =
312 QRegularExpression(QString("([+-]?)([A-Z][a-z]*)(\\d*[\\.]?\\d*)"));
313
314 /*!
315 \brief Regular expression that matches the end of line in text files.
316 \ingroup Globals
317 */
318 QRegularExpression gEndOfLineRegExp = QRegularExpression("^\\s+$");
319
320 /*!
321 \brief Regular expression that matches the m/z,i pairs in text files.
322 \ingroup Globals
323 */
324 QRegularExpression gXyFormatMassDataRegExp =
325 QRegularExpression("^(\\d*\\.?\\d+)([^\\d^\\.^-]+)(-?\\d*\\.?\\d*[e-]?\\d*)");
326
327 /*!
328 \brief Number of decimal places after the decimal symbol for atom masses.
329 \ingroup Globals
330 */
331 int ATOM_DEC_PLACES = 10;
332
333 /*!
334 \brief Number of decimal places after the decimal symbol for oligomer masses.
335 \ingroup Globals
336 */
337 int OLIGOMER_DEC_PLACES = 5;
338
339 /*!
340 \brief Number of decimal places after the decimal symbol for polymer masses.
341 \ingroup Globals
342 */
343 int POLYMER_DEC_PLACES = 3;
344
345 /*!
346 \brief Number of decimal places after the decimal symbol for pH/pKa values.
347 \ingroup Globals
348 */
349 int PH_PKA_DEC_PLACES = 2;
350
351
352 /*!
353 \brief Returns the average of the \a list of \c double values.
354 \ingroup Globals
355
356 All the values in list are process in order to compute the following:
357
358 \list
359 \li \a sum
360 \li \a average
361 \li \a variance
362 \li \a stdDeviation
363 \li \a nonZeroSmallest
364 \li \a smallest
365 \li \a smallestMedian
366 \li \a greatest
367 \endlist
368
369 statistical values if the corresponding parameters are non-nullptr.
370
371 \sa doubleVectorStatistics()
372 */
373 void
374 doubleListStatistics(QList<double> list,
375 double *sum,
376 double *average,
377 double *variance,
378 double *stdDeviation,
379 double *nonZeroSmallest,
380 double *smallest,
381 double *smallestMedian,
382 double *greatest)
383 {
384 if(sum == Q_NULLPTR || average == Q_NULLPTR || variance == Q_NULLPTR ||
385 stdDeviation == Q_NULLPTR || nonZeroSmallest == Q_NULLPTR ||
386 smallest == Q_NULLPTR || greatest == Q_NULLPTR ||
387 smallestMedian == Q_NULLPTR)
388 {
389 qFatal(
390 "Fatal error at %s@%d -- %s(). "
391 "Pointers cannot be nullptr."
392 "Program aborted.",
393 __FILE__,
394 __LINE__,
395 __FUNCTION__);
396 }
397
398 // Sort the list, we'll need it sorted to compute the median value.
399 std::sort(list.begin(), list.end());
400
401 int count = list.size();
402
403 if(count == 0)
404 return;
405
406 // Sorted list, the smallest is the first.
407 *smallest = list.first();
408 // The greatest is the last.
409 *greatest = list.last();
410
411 *sum = 0;
412 *nonZeroSmallest = std::numeric_limits<double>::max();
413
414 for(int iter = 0; iter < count; ++iter)
415 {
416 double current = list.at(iter);
417 *sum += current;
418
419 // If current is non-zero, then take its value into account.
420 if(current > 0 && current < *nonZeroSmallest)
421 *nonZeroSmallest = current;
422 }
423
424 *average = *sum / count;
425
426 double varN = 0;
427
428 for(int iter = 0; iter < count; ++iter)
429 {
430 varN += (list.at(iter) - *average) * (list.at(iter) - *average);
431 }
432
433 *variance = varN / count;
434 *stdDeviation = std::sqrt(*variance);
435
436 // Now the median value
437
438 // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
439 //<< "count:" << count;
440
441 if(count == 1)
442 {
443 *smallestMedian = list.first();
444 }
445 else
446 {
447 if(count % 2 == 0)
448 *smallestMedian = (list.at(count / 2 - 1) + list.at(count / 2)) / 2;
449 else
450 *smallestMedian = list.at(count / 2 + 1);
451 }
452 }
453
454
455 /*!
456 \brief Returns the average of the \a vector of \c double values.
457 \ingroup Globals
458
459 All the values in list are process in order to compute the following:
460
461 \list
462 \li \a sum
463 \li \a average
464 \li \a variance
465 \li \a stdDeviation
466 \li \a nonZeroSmallest
467 \li \a smallest
468 \li \a smallestMedian
469 \li \a greatest
470 \endlist
471
472 statistical values if the corresponding parameters are non-nullptr.
473
474 \sa doubleListStatistics()
475 */
476 void
477 doubleVectorStatistics(std::vector<double> &vector,
478 double *sum,
479 double *average,
480 double *variance,
481 double *stdDeviation,
482 double *nonZeroSmallest,
483 double *smallest,
484 double *smallestMedian,
485 double *greatest)
486 {
487 if(sum == Q_NULLPTR || average == Q_NULLPTR || variance == Q_NULLPTR ||
488 stdDeviation == Q_NULLPTR || nonZeroSmallest == Q_NULLPTR ||
489 smallest == Q_NULLPTR || greatest == Q_NULLPTR ||
490 smallestMedian == Q_NULLPTR)
491 qFatal("Programming error.");
492
493 // Sort the vector, we'll need it sorted to compute the median value.
494 std::sort(vector.begin(), vector.end());
495
496 int count = vector.size();
497
498 if(!count)
499 return;
500
501 // Sorted vector, the smallest is the first.
502 *smallest = vector.front();
503 // The greatest is the last.
504 *greatest = vector.back();
505
506 *sum = 0;
507 *nonZeroSmallest = std::numeric_limits<double>::max();
508
509 for(auto &&value : vector)
510 {
511 double current = value;
512 *sum += current;
513
514 // If current is non-zero, then take its value into account.
515 if(current > 0 && current < *nonZeroSmallest)
516 *nonZeroSmallest = current;
517 }
518
519 *average = *sum / count;
520
521 double varN = 0;
522
523 for(auto &&value : vector)
524 {
525 varN += (value - *average) * (value - *average);
526 }
527
528 *variance = varN / count;
529 *stdDeviation = sqrt(*variance);
530
531 // Now the median value
532
533 // qDebug() << "count:" << count;
534
535 if(count == 1)
536 {
537 *smallestMedian = vector.front();
538 }
539 else
540 {
541 if(count % 2 == 0)
542 *smallestMedian = (vector.at(count / 2 - 1) + vector.at(count / 2)) / 2;
543 else
544 *smallestMedian = vector.at(count / 2 + 1);
545 }
546 }
547
548
549 /*!
550 \brief Returns true if both double values \a value1 and \a value2, are equal
551 within the double representation capabilities of the platform, false otherwise.
552 \ingroup Globals
553
554 In the comparison, the \a decimal_places are taken into account.
555 */
556 bool
557 almostEqual(double value1, double value2, int decimal_places)
558 {
559 // QString value1String = QString("%1").arg(value1,
560 // 0, 'f', 60);
561 // QString value2String = QString("%1").arg(value2,
562 // 0, 'f', 60);
563
564 // qWarning() << __FILE__ << __LINE__ << __FUNCTION__
565 //<< "value1:" << value1String << "value2:" << value2String;
566
567 // The machine epsilon has to be scaled to the magnitude of the values used
568 // and multiplied by the desired precision in ULPs (units in the last place)
569 // (decimal places).
570
571 double valueSum = std::abs(value1 + value2);
572 // QString valueSumString = QString("%1").arg(valueSum,
573 // 0, 'f', 60);
574
575 double valueDiff = std::abs(value1 - value2);
576 // QString valueDiffString = QString("%1").arg(valueDiff,
577 // 0, 'f', 60);
578
579 double epsilon = std::numeric_limits<double>::epsilon();
580 // QString epsilonString = QString("%1").arg(epsilon,
581 // 0, 'f', 60);
582
583 double scaleFactor = epsilon * valueSum * decimal_places;
584 // QString scaleFactorString = QString("%1").arg(scaleFactor,
585 // 0, 'f', 60);
586
587 // qWarning() << "valueDiff:" << valueDiffString << "valueSum:" <<
588 // valueSumString << "epsilon:" << epsilonString << "scaleFactor:" <<
589 // scaleFactorString;
590
591 bool res = valueDiff < scaleFactor
592 // unless the result is subnormal:
593 || valueDiff < std::numeric_limits<double>::min();
594
595 // qWarning() << __FILE__ << __LINE__ << __FUNCTION__
596 //<< "returning res:" << res;
597
598 return res;
599 }
600
601
602 /*!
603 \brief Provides a text stream handle to the standard output.
604 \ingroup Globals
605
606 Use:
607
608 \code
609 qStdOut() << __FILE__ << __LINE__
610 << "text to the standard output,"
611 << "not the error standard output."
612 \endcode
613
614 Returns a reference to a static QTextStream that directs to the standard
615 output.
616 */
617 QTextStream &
618 qStdOut()
619 {
620 static QTextStream ts(stdout);
621 return ts;
622 }
623
624
625 /*!
626 \brief Removes all space characters from the \a text string (the removal is
627 in-place).
628 \ingroup Globals
629
630 Returns a reference to the in-place-modified string.
631 */
632 QString &
633 24 unspacifyString(QString &text)
634 {
635
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
24 if(text.isEmpty())
636 {
637 return text;
638 }
639
640
3/6
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 24 times.
✗ Branch 8 not taken.
24 text.remove(QRegularExpression("\\s+"));
641
642 24 return text;
643 }
644
645
646 /*!
647 \brief Returns a string with a binary representation of the \a value integer.
648 \ingroup Globals
649 */
650 QString
651 binaryRepresentation(int value)
652 {
653 QString string;
654 string = QString("%1").arg(value, 32, 2);
655
656 return string;
657 }
658
659
660 /*!
661 \brief Returns a shortened (elided) version of the \a text string.
662 \ingroup Globals
663
664 It is sometimes necessary to display, in a graphical user interface a very long
665 string, that cannot fit in the provided widget. This function returns a
666 shortened version of the input string.
667
668 For example, "Interesting bits of information are often lost where there
669 are too many details", becomes "Interes...details".
670
671 \a chars_left: Count of characters to be kept on the left side of the string.
672
673 \a chars_right: Count of characters to be kept on the right side of the string.
674
675 \a delimiter string to use as the elision delimiter (in the above example, that
676 is '.').
677 */
678 QString
679 elideText(const QString &text,
680 int chars_left,
681 int chars_right,
682 const QString &delimiter)
683 {
684 // We want to elide text. For example, imagine we have text = "that
685 // is beautiful stuff", with charsLeft 4 and charsRight 4 and
686 // delimitor "...". Then the result would be "that...tuff"
687
688 if(chars_left < 1 || chars_right < 1)
689 {
690 return text;
691 }
692
693 int size = text.size();
694
695 // If the text string is already too short, no need to elide
696 // anything.
697 if((chars_left + chars_right + delimiter.size()) >= size)
698 {
699 return text;
700 }
701
702 QString result = text.left(chars_left);
703 result.append(delimiter);
704 result.append(text.right(chars_right));
705
706 return result;
707 }
708
709
710 /*!
711 \brief Returns a string containing a paragraph-based version of the very long
712 single-line \a text.
713 \ingroup Globals
714
715 When a text string is too long to be displayed in a line of reasonable
716 length, inserts newline characters at positions calculated to yield a
717 paragraph of the given \a width.
718
719 \sa stanzifyParagraphs()
720 */
721 QString
722 stanzify(const QString &text, int width)
723 {
724 QString result = text;
725
726 // First, replace all the existing newline chars with spaces.
727
728 result = result.replace("\n", " ");
729
730 int iter = width;
731
732 // Then, iterate in the obtained string and every width characters try to
733 // insert a newline character by iterating back to the left and searching
734 // for a space.
735
736 for(; iter < result.size(); iter += width)
737 {
738 // Now iterate in reverse and search for a space where to insert a
739 // newline
740
741 int jter = iter;
742
743 for(; jter >= 0; --jter)
744 {
745 if(result.at(jter) == ' ')
746 {
747 result[jter] = '\n';
748 break;
749 }
750 }
751 }
752
753 return result;
754 }
755
756
757 /*!
758 \brief Returns a string containing a series of paragraph-based versions of the
759 very long single-line-containing paragraphs in \a text.
760 \ingroup Globals
761
762 \a text is a string with newline characters that delimit paragraph that
763 thelmselves are made of a very long single line. This function splits \a text
764 along the \c '\n' character and each obtained very long single-line string is
765 reduced to a set of lines to make a pararagraph (see \l{stanzify}). The with of
766 the line in that generated paragraph is \a width.
767
768 \sa stanzify()
769 */
770 QString
771 stanzifyParagraphs(const QString &text, int width)
772 {
773 QString result;
774
775 QStringList paragraphList = text.split("\n");
776
777 for(int iter = 0; iter < paragraphList.size(); ++iter)
778 {
779 QString line = paragraphList.at(iter);
780
781 QString stanzifiedLine = stanzify(line, width);
782
783 result.append(stanzifiedLine);
784 result.append("\n");
785 }
786
787 return result;
788 }
789
790
791 /*!
792 \brief Returns the number of zero decimals in \a value that are found between
793 the decimal point and the first non-zero decimal.
794 \ingroup Globals
795
796 For example, 0.11 would return 0 (no empty decimal)
797
798 2.001 would return 2
799
800 1000.0001254 would return 3
801 */
802 int
803 countZeroDecimals(double value)
804 {
805
806 int intPart = static_cast<int>(value);
807
808 double decimalPart = value - intPart;
809
810 int count = -1;
811
812 while(1)
813 {
814 ++count;
815
816 decimalPart *= 10;
817
818 if(decimalPart > 1)
819 return count;
820 }
821
822 return count;
823 }
824
825
826 /*!
827 \brief Returns the delta value corresponding to \a value and \a ppm
828 part-per-million.
829 \ingroup Globals
830 */
831 double
832 ppm(double value, double ppm)
833 {
834
835 return (value * ppm) / 1000000;
836 }
837
838
839 /*!
840 \brief Returns \a value incremented by the matching \a ppm part.
841 \ingroup Globals
842 */
843 double
844 addPpm(double value, double ppm)
845 {
846
847 return value + (value * ppm) / 1000000;
848 }
849
850
851 /*!
852 \brief Returns \a value decremented by the matching \a ppm part.
853 \ingroup Globals
854 */
855 double
856 removePpm(double value, double ppm)
857 {
858 return value - (value * ppm) / 1000000;
859 }
860
861
862 /*!
863 \brief Return the delta corresponding to \a value and resolution \a res.
864 \ingroup Globals
865 */
866 double
867 res(double value, double res)
868 {
869 return value / res;
870 }
871
872
873 /*!
874 \brief Returns \a value incremented by the matching \a res part.
875 \ingroup Globals
876 */
877 double
878 addRes(double value, double res)
879 {
880 return value + (value / res);
881 }
882
883 /*!
884 \brief Returns \a value decremented by the matching \a res part.
885 \ingroup Globals
886 */
887 double
888 removeRes(double value, double res)
889 {
890 return value - (value / res);
891 }
892
893
894 /*!
895 \brief Returns a string representing the \a pointer address location (for
896 debugging).
897 \ingroup Globals
898 */
899 QString
900 pointerAsString(void *pointer)
901 {
902 return QString("%1").arg(
903 (quintptr)pointer, QT_POINTER_SIZE * 2, 16, QChar('0'));
904 }
905
906 /*!
907 \brief Returns a string holding the the file path to the configuration settings
908 for applicationĀ \a module_name.
909 \ingroup Globals
910 */
911 QString
912 configSettingsFilePath(const QString &module_name)
913 {
914
915 // The configuration settings file for all the settings of the program
916
917 QString file_path =
918 QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
919
920 if(file_path.isEmpty())
921 file_path = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
922
923 file_path = file_path + QDir::separator() + module_name;
924
925 file_path = file_path + QDir::separator() + "configSettings.ini";
926
927 return file_path;
928 }
929
930 /*!
931 \brief Returns 0 if both files \a file_path_1 and \a file_path_2 have the same
932 contents or 1 if theirs contents differ.
933
934 Returns -1 if any file could not be opened.
935 */
936 int
937 4 testDiffBetweenTwoTextFiles(const QString &file_path_1,
938 const QString &file_path_2)
939 {
940
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 QFile file_1(file_path_1);
941
2/4
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
4 if(!file_1.open(QIODevice::ReadOnly | QIODevice::Text))
942 {
943 qDebug() << file_path_1 << "could not be opened";
944 return -1;
945 }
946
947
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 QFile file_2(file_path_2);
948
2/4
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
4 if(!file_2.open(QIODevice::ReadOnly | QIODevice::Text))
949 {
950 qDebug() << file_path_2 << "could not be opened";
951 return -1;
952 }
953
954
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 QTextStream in_stream_1(&file_1), in_stream_2(&file_2);
955
956
7/10
✓ Branch 1 taken 1696 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1692 times.
✓ Branch 4 taken 4 times.
✓ Branch 6 taken 1692 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1692 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1692 times.
✓ Branch 11 taken 4 times.
1696 while(!in_stream_1.atEnd() && !in_stream_2.atEnd())
957 {
958
1/2
✓ Branch 1 taken 1692 times.
✗ Branch 2 not taken.
1692 QString line_1 = in_stream_1.readLine();
959
1/2
✓ Branch 1 taken 1692 times.
✗ Branch 2 not taken.
1692 QString line_2 = in_stream_2.readLine();
960
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1692 times.
1692 if(line_1 != line_2)
961 return 1;
962
2/4
✓ Branch 1 taken 1692 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1692 times.
✗ Branch 5 not taken.
1692 }
963
964 4 return 0;
965 4 }
966
967 } // namespace libXpertMass
968 } // namespace MsXpS
969