GCC Code Coverage Report


./
File: src/XpertMass/Coordinates.cpp
Date: 2024-08-24 11:26:06
Lines:
44/219
20.1%
Functions:
11/32
34.4%
Branches:
14/252
5.6%

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 <QDebug>
36 #include <QStringList>
37
38
39 /////////////////////// Local includes
40 #include "Coordinates.hpp"
41
42
43 namespace MsXpS
44 {
45
46 namespace libXpertMass
47 {
48
49 /*!
50 \class MsXpS::libXpertMass::Coordinates
51 \inmodule libXpertMass
52 \ingroup PolChemDefBuildingdBlocks
53 \inheaderfile Coordinates.hpp
54
55 \brief The Coordinates class provides the localization of a sequence region
56 in a \l Polymer \l Sequence.
57
58 The localization of the sequence region is performed by using \e indices
59 pointing to the location in the Polymer sequence.
60 */
61
62 /*!
63 \variable MsXpS::libXpertMass::Coordinates::m_start
64
65 \brief The index of the first monomer in the
66 sequence region described by this Coordinates instance.
67 */
68
69 /*!
70 \variable MsXpS::libXpertMass::Coordinates::m_end
71
72 \brief The index of the last monomer in the
73 sequence region described by this Coordinates instance.
74 */
75
76
77 ///////////////////////// Coordinates /////////////////////////
78 ///////////////////////// Coordinates /////////////////////////
79 ///////////////////////// Coordinates /////////////////////////
80 ///////////////////////// Coordinates /////////////////////////
81 ///////////////////////// Coordinates /////////////////////////
82
83 /*!
84 \brief Constructs a Coordinates object with \a index_start and \a index_end
85 indices.
86
87 The indices define a region in the \l Sequence. They are sorted so as to
88 ensure that index_start <= index_end.
89 */
90 72 Coordinates::Coordinates(int index_start, int index_end)
91 {
92
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 72 times.
72 if(index_start > index_end)
93 {
94 m_start = index_end;
95 m_end = index_start;
96 }
97 else
98 {
99 72 m_start = index_start;
100 72 m_end = index_end;
101 }
102 72 }
103
104
105 /*!
106 \brief Constructs a Coordinates object as a copy of \a other.
107
108 After assigning \a other to this Coordinates, the indices are sorted so as to
109 ensure that index_start <= index_end.
110 */
111 216 Coordinates::Coordinates(const Coordinates &other)
112 216 : m_start(other.m_start), m_end(other.m_end)
113 {
114 int temp;
115
116
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 216 times.
216 if(m_start > m_end)
117 {
118 temp = m_end;
119 m_end = m_start;
120 m_start = temp;
121 }
122 216 }
123
124 /*!
125 \brief Destructs this Coordinates instance.
126 */
127 288 Coordinates::~Coordinates()
128 {
129 288 }
130
131 /*!
132 \brief Set the start index to \a value.
133
134 Ensures that m_start is <= m_end.
135 */
136 void
137 Coordinates::setStart(int value)
138 {
139 if(value > m_end)
140 {
141 m_start = m_end;
142 m_end = value;
143 }
144 else
145 {
146 m_start = value;
147 }
148 }
149
150 /*!
151 \brief Returns the member start index.
152 */
153 int
154 192 Coordinates::start() const
155 {
156 192 return m_start;
157 }
158
159 /*!
160 \brief Increments by one unit the member end index.
161 */
162 void
163 Coordinates::incrementEnd()
164 {
165 ++m_end;
166 }
167
168
169 /*!
170 \brief Set the end index to \a value.
171
172 Ensures that m_start is <= m_end.
173 */
174 void
175 Coordinates::setEnd(int value)
176 {
177 m_end = value;
178 }
179
180
181 /*!
182 \brief Returns the member end index.
183 */
184 int
185 5808 Coordinates::end() const
186 {
187 5808 return m_end;
188 }
189
190 /*!
191 \brief Returns the number of monomer codes in the region described by this
192 Coordinates instance.
193
194 \code
195 return (m_end - m_start + 1)
196 \endcode
197 */
198 int
199 Coordinates::length() const
200 {
201 return (m_end - m_start + 1);
202 }
203
204 /*!
205 \brief Returns a string with the region described by this Coordinates
206 instance.
207
208 The values are the member indices and the format is
209
210 \code
211 [125--259]
212 \endcode
213 */
214 QString
215 Coordinates::indicesAsText() const
216 {
217 QString text = QString("[%1--%2]").arg(m_start).arg(m_end);
218
219 return text;
220 }
221
222
223 /*!
224 \brief Returns a string with the region described by this Coordinates
225 instance.
226
227 The values are the member indices \e{incremented} by one unit (thus being
228 \e{positions} and not \e{indices}) and the format is
229
230 \code
231 [126--260]
232 \endcode
233 */
234 QString
235 Coordinates::positionsAsText() const
236 {
237 QString text = QString("[%1--%2]").arg(m_start + 1).arg(m_end + 1);
238
239 return text;
240 }
241
242 /*!
243 \brief Resets the member indices to 0.
244 */
245 void
246 Coordinates::reset()
247 {
248 m_start = 0;
249 m_end = 0;
250 }
251
252
253 /*!
254 \class MsXpS::libXpertMass::CoordinateList
255 \inmodule libXpertMass
256 \ingroup PolChemDefBuildingdBlocks
257
258 \brief The CoordinateList class provides a list of \l Coordinates
259 allocated instances.
260
261 The Coordinates instances are allocated on the heap and stored as a QList.
262 */
263
264 /*!
265 \variable int MsXpS::libXpertMass::CoordinateList::m_comment
266
267 \brief A comment string.
268 */
269
270 ///////////////////////// CoordinateList /////////////////////////
271 ///////////////////////// CoordinateList /////////////////////////
272 ///////////////////////// CoordinateList /////////////////////////
273 ///////////////////////// CoordinateList /////////////////////////
274 ///////////////////////// CoordinateList /////////////////////////
275
276 /*!
277 \brief Constructs a CoordinateList using \a comment and \a list.
278
279 The \l Coordinates instances in \a list are duplicated and stored in this
280 CoordinateList.
281 */
282 144 CoordinateList::CoordinateList(QString comment, QList<Coordinates *> *list)
283 144 : m_comment(comment)
284 {
285
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 144 times.
144 if(list)
286 {
287 for(int iter = 0; iter < list->size(); ++iter)
288 {
289 Coordinates *iterCoordinates = list->at(iter);
290
291 Coordinates *coordinates = new Coordinates(*iterCoordinates);
292
293 append(coordinates);
294 }
295 }
296 144 }
297
298 /*!
299 \brief Constructs a CoordinateList as a copy of \a other.
300
301 The copy is a deep copy, with all the \l Coordinates instances in \a other
302 duplicated and stored in this CoordinateList.
303 */
304 CoordinateList::CoordinateList(const CoordinateList &other)
305 : QList(), m_comment(other.m_comment)
306 {
307 for(int iter = 0; iter < other.size(); ++iter)
308 {
309 Coordinates *iterCoordinates = other.at(iter);
310
311 Coordinates *coordinates = new Coordinates(*iterCoordinates);
312
313 append(coordinates);
314 }
315 }
316
317 /*!
318 \brief Destructs this CoordinateList.
319
320 The \l Coordinates instances are freed.
321 */
322 144 CoordinateList::~CoordinateList()
323 {
324 // The members of the list were allocated with new()...
325 144 qDeleteAll(begin(), end());
326 144 clear();
327 144 }
328
329 /*!
330 \brief Assigns to this CoordinateList the members of \a other.
331
332 The copy involves duplicationg all the Coordinates objects in \a other and all
333 other members.
334
335 Returns a reference to this CoordinateList instance.
336 */
337 CoordinateList &
338 CoordinateList::operator=(const CoordinateList &other)
339 {
340 if(&other == this)
341 return *this;
342
343 m_comment = other.m_comment;
344
345 empty();
346
347 for(int iter = 0; iter < other.size(); ++iter)
348 {
349 Coordinates *iterCoordinates = other.at(iter);
350
351 Coordinates *coordinates = new Coordinates(*iterCoordinates);
352
353 append(coordinates);
354 }
355
356 return *this;
357 }
358
359 /*!
360 \brief Adds a copy of \a coordinates to this CoordinateList instance.
361
362 \note This CoordinateList's list of Coordinates instances is first emptied,
363 essentially replacing its contents with a copy of \a coordinates.
364 */
365 void
366 72 CoordinateList::setCoordinates(const Coordinates &coordinates)
367 {
368 72 empty();
369
370
1/4
✓ Branch 2 taken 72 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
72 Coordinates *newCoordinates = new Coordinates(coordinates);
371 72 append(newCoordinates);
372 72 }
373
374
375 /*!
376 \brief Allocates a copy of each Coordinates instance in \a list and adds
377 it to this CoordinateList instance.
378
379 \note This CoordinateList's list of Coordinates instances is first emptied,
380 essentially replacing its contents with a copy of those in \a list.
381 */
382 void
383 72 CoordinateList::setCoordinates(const CoordinateList &list)
384 {
385 72 empty();
386
387 // qDebug() << "The list of Coordinates:" << list.size();
388
389
2/2
✓ Branch 1 taken 72 times.
✓ Branch 2 taken 72 times.
144 for(int iter = 0; iter < list.size(); ++iter)
390 {
391
1/4
✓ Branch 3 taken 72 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
72 Coordinates *coordinates = new Coordinates(*list.at(iter));
392
393 // qDebug() << "Created new copy of Coordinates:"
394 // << coordinates->indicesAsText();
395
396 72 append(coordinates);
397 }
398 72 }
399
400
401 /*!
402 \brief Add a copy of \a coordinates this CoordinateList instance.
403 */
404 void
405 CoordinateList::appendCoordinates(const Coordinates &coordinates)
406 {
407 Coordinates *newCoordinates = new Coordinates(coordinates);
408
409 append(newCoordinates);
410 }
411
412 /*!
413 \brief Creates the Coordinates instances based on \a coordinates_string and add
414 them to this CoordinateList's list of Coordinates.
415
416 \note This CoordinateList's list of Coordinates instances is first emptied
417
418 The format of the \a coordinates_string is
419
420 \code
421 "[228-246]"
422 \endcode
423
424 if there are not multiple regions. If there are multiple regions (for example
425 when a cross-link exists), the format changes to account for the multiple
426 regions:
427
428 \code
429 "[228-246][276-282][247-275]"
430 \endcode
431
432 \note It is expected that the values in the coordinates_string are \e positions
433 strings and not \e indices.
434
435 Returns the count of added Coordinates instances or -1 if an error occurred.
436 */
437 int
438 CoordinateList::setCoordinates(const QString &coordinates_string)
439 {
440 // We get a string in the form [xxx-yyy] (there can be more than
441 // one such element, if cross-linked oligomers are calculated.
442 // "[228-246][276-282][247-275]". Because that string comes from
443 // outside of massXpert, it is expected that it contains positions
444 // and not indexes. So we have to decrement the start and end
445 // values by one.
446
447 // qDebug() <<__FILE__ << __LINE__
448 // << "string:" << string;
449
450 // Whatever will happen, we wanto set coordinates, not append,
451 // thus first empty().
452 empty();
453
454 if(!coordinates_string.contains('[') || !coordinates_string.contains(']') ||
455 !coordinates_string.contains('-'))
456 return -1;
457
458 QStringList coordinateList =
459 coordinates_string.split(']', Qt::SkipEmptyParts);
460
461 // qDebug() << __FILE__ << __LINE__
462 // << "coordinateList:" << coordinateList;
463
464 for(int iter = 0; iter < coordinateList.size(); ++iter)
465 {
466 QString coordinates = coordinateList.at(iter);
467
468 coordinates = coordinates.remove('[');
469
470 QStringList positionList = coordinates.split('-');
471
472 // qDebug() << __FILE__ << __LINE__
473 // << "positionList:" << positionList;
474
475 if(positionList.size() != 2)
476 {
477 // Error.
478 // qDebug() << __FILE__<< __LINE__
479 // << "error: return -1";
480
481 return -1;
482 }
483
484 // At this point we should have two numeric values in the the
485 // positionList.
486 bool ok = false;
487 int start = positionList.at(0).toInt(&ok);
488
489 // The index might well be 0, but the, ok should be true.
490
491 if(!--start && !ok)
492 return -1;
493
494 ok = false;
495 int end = positionList.at(1).toInt(&ok);
496
497 if(!--end && !ok)
498 return -1;
499
500 Coordinates *newCoordinates = 0;
501
502 if(start > end)
503 newCoordinates = new Coordinates(end, start);
504 else
505 newCoordinates = new Coordinates(start, end);
506
507 append(newCoordinates);
508 }
509
510 QString text = positionsAsText();
511
512 // qDebug() << __FILE__ << __LINE__
513 // << "positionsAsText: " << text;
514
515 return size();
516 }
517
518 /*!
519 \brief Sets the comment to \a text.
520 */
521 void
522 CoordinateList::setComment(QString text)
523 {
524 m_comment = text;
525 }
526
527 /*!
528 \brief Returns the comment.
529 */
530 QString
531 CoordinateList::comment() const
532 {
533 return m_comment;
534 }
535
536 /*!
537 \brief Searches all the Coordinates that have the smallest m_start value.
538
539 Searches all the Coordinates instances in this CoordinateList that share the
540 same Coordinates::m_start value that is actually the smallest such value in the
541 list. Each found Coordinates instance's index in this CoordinateList is added
542 to \a index_list.
543
544 \note \a index_list is first emptied.
545
546 Returns the count of Coordinates instances added to \a index_list.
547
548 \sa rightMostCoordinates(), isLeftMostCoordinates(), isRightMostCoordinates
549 */
550 int
551 CoordinateList::leftMostCoordinates(QList<int> &index_list) const
552 {
553 if(isEmpty())
554 return 0;
555
556 while(!index_list.isEmpty())
557 index_list.removeFirst();
558
559 int leftMostValue = first()->start();
560
561 for(int iter = 0; iter < size(); ++iter)
562 {
563 Coordinates *coordinates = at(iter);
564
565 int start = coordinates->start();
566
567 if(start < leftMostValue)
568 leftMostValue = start;
569 }
570
571 // At this point we now what's the leftmost index. We can use that
572 // index to now search for all the items that are also leftmost.
573
574 for(int iter = 0; iter < size(); ++iter)
575 {
576 if(at(iter)->start() == leftMostValue)
577 index_list.append(iter);
578 }
579
580 return index_list.size();
581 }
582
583 /*!
584 \brief Returns true if \a coordinates is the left-most Coordinates in this
585 CoordinateList. false otherwise.
586 */
587 bool
588 CoordinateList::isLeftMostCoordinates(Coordinates *coordinates) const
589 {
590 Q_ASSERT(coordinates);
591
592 // Are the coordinates the leftmost coordinates of *this
593 // CoordinateList ?
594
595 int value = coordinates->start();
596
597 for(int iter = 0; iter < size(); ++iter)
598 {
599 Coordinates *coordinates = at(iter);
600
601 if(value > coordinates->start())
602 return false;
603 }
604
605 return true;
606 }
607
608
609 /*!
610 \brief Searches all the Coordinates that have the greatest m_end value.
611
612 Searches all the Coordinates instances in this CoordinateList that share the
613 same Coordinates::m_end value that is actually the greatest such value in the
614 list. Each found Coordinates instance's index in this CoordinateList is added
615 to \a index_list.
616
617 \note \a index_list is first emptied.
618
619 Returns the count of Coordinates instances added to \a index_list.
620
621 \sa leftMostCoordinates(), isLeftMostCoordinates(), isRightMostCoordinates
622 */
623 int
624 CoordinateList::rightMostCoordinates(QList<int> &index_list) const
625 {
626 if(isEmpty())
627 return 0;
628
629 while(!index_list.isEmpty())
630 index_list.removeFirst();
631
632 int rightMostValue = first()->end();
633
634 for(int iter = 0; iter < size(); ++iter)
635 {
636 Coordinates *coordinates = at(iter);
637
638 int end = coordinates->end();
639
640 if(end > rightMostValue)
641 rightMostValue = end;
642 }
643
644 // At this point we now what's the rightmost index. We can use
645 // that index to now search for all the items that are also
646 // rightmost.
647
648 for(int iter = 0; iter < size(); ++iter)
649 {
650 if(at(iter)->end() == rightMostValue)
651 index_list.append(iter);
652 }
653
654 return index_list.size();
655 }
656
657
658 /*!
659 \brief Returns true if \a coordinates is the right-most Coordinates in this
660 CoordinateList. false otherwise.
661 */
662 bool
663 CoordinateList::isRightMostCoordinates(Coordinates *coordinates) const
664 {
665 Q_ASSERT(coordinates);
666
667 // Are the coordinates the rightmost coordinates of *this
668 // CoordinateList ?
669
670 int value = coordinates->end();
671
672 for(int iter = 0; iter < size(); ++iter)
673 {
674 Coordinates *coordinates = at(iter);
675
676 if(value < coordinates->end())
677 return false;
678 }
679
680 return true;
681 }
682
683 /*!
684 \brief Returns true if \a index is found to be between the start and end
685 indices of at least one Coordinates instance in this CoordinateList, false
686 otherwise.
687 */
688 bool
689 48 CoordinateList::encompassIndex(int index) const
690 {
691
2/2
✓ Branch 1 taken 48 times.
✓ Branch 2 taken 8 times.
56 for(int iter = 0; iter < size(); ++iter)
692 {
693 48 Coordinates *coordinates = at(iter);
694
695
5/6
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 40 times.
✓ Branch 5 taken 8 times.
✓ Branch 6 taken 40 times.
✓ Branch 7 taken 8 times.
48 if(index >= coordinates->start() && index <= coordinates->end())
696 40 return true;
697 }
698
699 8 return false;
700 }
701
702 /*!
703 \brief Returns true if at least two Coordinates instances overlap.
704
705 Two Coordinates instances overlap if the second's m_start member is less than
706 the first's m_end and greater than the first's m_start.
707
708 \sa encompassIndex()
709 */
710 bool
711 CoordinateList::overlap() const
712 {
713 // Return true if there are overlapping regions in this
714 // coordinate list.
715
716 if(size() <= 1)
717 return false;
718
719 for(int iter = 0; iter < size(); ++iter)
720 {
721 Coordinates coords1 = *at(iter);
722
723 int start1 = coords1.start();
724 int end1 = coords1.end();
725
726 for(int jter = 0; jter < size(); ++jter)
727 {
728 // Do not compare one item to itself.
729 if(jter == iter)
730 continue;
731
732 Coordinates coords2 = *at(jter);
733
734 int start2 = coords2.start();
735
736 if(start2 <= end1 && start2 >= start1)
737 return true;
738 }
739 }
740
741 return false;
742 }
743
744 /*!
745 \brief Returns a string documenting the Coordinates in this CoordinateList.
746
747 Each Coordinates instance is described like the following with the values being
748 the indices m_start and m_end:
749
750 \code
751 [156-350]
752 \endcode
753
754 \sa positionsAsText()
755 */
756 QString
757 CoordinateList::indicesAsText() const
758 {
759 QString text;
760
761 for(int iter = 0; iter < size(); ++iter)
762 {
763 Coordinates *coordinates = at(iter);
764
765 text +=
766 QString("[%1-%2]").arg(coordinates->start()).arg(coordinates->end());
767 }
768
769 return text;
770 }
771
772
773 /*!
774 \brief Returns a string documenting the Coordinates in this CoordinateList.
775
776 Each Coordinates instance is described like the following with the values being
777 the indices m_start+1 and m_end+1:
778
779 \code
780 [157-351]
781 \endcode
782
783 \note The values reported are not \e indices, but \e positions.
784
785 \sa indicesAsText()
786 */
787 QString
788 CoordinateList::positionsAsText() const
789 {
790 QString text;
791
792 for(int iter = 0; iter < size(); ++iter)
793 {
794 Coordinates *coordinates = at(iter);
795
796 text += QString("[%1-%2]")
797 .arg(coordinates->start() + 1)
798 .arg(coordinates->end() + 1);
799 }
800
801 return text;
802 }
803
804 /*!
805 \brief Clears this CoordinateList of all its Coordinates.
806
807 The Coordinates instances are \c{free}'d.
808 */
809 void
810 144 CoordinateList::empty()
811 {
812 144 qDeleteAll(begin(), end());
813 144 clear();
814 144 }
815
816 /*!
817 \brief Outputs a string listing all the Coordinates using qDebug().
818 */
819 void
820 CoordinateList::debugPutStdErr()
821 {
822 qDebug() << __FILE__ << __LINE__ << "CoordinateList:";
823
824 QString text;
825
826 for(int iter = 0; iter < size(); ++iter)
827 {
828 Coordinates *coordinates = at(iter);
829
830 text += coordinates->indicesAsText();
831 }
832
833 qDebug() << __FILE__ << __LINE__ << text;
834 }
835
836 } // namespace libXpertMass
837
838 } // namespace MsXpS
839